一、需求
之前一篇博客使用ViewPager实现轮播图《Android ViewPager实现循环轮播图》,但是ViewPager有个天生的缺陷是View无法重用,此外ViewPager的滑动过程会频繁requestLayout,尽管可以通过addViewInLayout和removeViewInLayout配合PagerAdapter 的startUpdate和finishUpdate可以减少重绘,但在ListView和RecyclerView中仍然达不到最好的效果。因此,使用一种新的方式十分必要。
二、代码实现
RecyclerPagerView
public class RecyclerPagerView extends RecyclerView implements Handler . Callback { private static final long TASK_TIMEOUT = 3000 ; public OnPageChangeListener onPageChangeListener; private final Handler mRecyclerHandler; private final int MSG_PLAY_NEXT = 112233 ; private volatile boolean isPlaying = false ; private boolean lastIsPlayState = false ; private int realPosition = - 1 ; public RecyclerPagerView (Context context) { this (context, null ); } public RecyclerPagerView (Context context, @Nullable AttributeSet attrs) { this (context, attrs, 0 ); } public RecyclerPagerView (Context context, @Nullable AttributeSet attrs, int defStyle) { super (context, attrs, defStyle); mRecyclerHandler = new Handler(Looper.getMainLooper(), this ); } public void setOnPageChangeListener (OnPageChangeListener onPageChangeListener) { this .onPageChangeListener = onPageChangeListener; if ( this .onPageChangeListener!= null ){ addOnScrollListener( this .onPageChangeListener); int currentItem = getCurrentItem(); this .onPageChangeListener.onPageSelection(currentItem); } } public int getCurrentItem () { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager(); return linearLayoutManager.findFirstVisibleItemPosition(); } public void setCurrentItem ( int position, boolean isAnimate) { Adapter adapter = getAdapter(); if (adapter== null || adapter.getItemCount()<=position){ return ; } if (!isAnimate) { scrollToPosition(position); } else { smoothScrollToPosition(position); } } public void setCurrentItem ( int position ) { setCurrentItem(position, true ); } @Override public boolean fling ( int velocityX, int velocityY) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager(); int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels; // views on the screen int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition(); View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition); int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition(); View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition); // distance we need to scroll int leftMargin = (screenWidth - lastView.getWidth()) / 2 ; int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth(); int leftEdge = lastView.getLeft(); int rightEdge = firstView.getRight(); int scrollDistanceLeft = leftEdge - leftMargin; int scrollDistanceRight = rightMargin - rightEdge; int targetPosition; if (Math.abs(velocityX) < 1500 ) { // The fling is slow -> stay at the current page if we are less than half through, // or go to the next page if more than half through if (leftEdge > screenWidth / 2 ) { // go to next page smoothScrollBy(-scrollDistanceRight, 0 ); targetPosition = firstVisibleItemPosition; } else if (rightEdge < screenWidth / 2 ) { // go to next page smoothScrollBy(scrollDistanceLeft, 0 ); targetPosition = firstVisibleItemPosition+ 1 ; } else { // stay at current page if (velocityX > 0 ) { smoothScrollBy(-scrollDistanceRight, 0 ); } else { smoothScrollBy(scrollDistanceLeft, 0 ); } targetPosition = firstVisibleItemPosition; } } else { // The fling is fast -> go to next page if (velocityX > 0 ) { smoothScrollBy(scrollDistanceLeft, 0 ); targetPosition = firstVisibleItemPosition+ 1 ; } else { smoothScrollBy(-scrollDistanceRight, 0 ); targetPosition = firstVisibleItemPosition; } } Log.e( "RecyclerPagerView" , "nextPage=" +targetPosition); if ( this .onPageChangeListener!= null ){ realPosition = targetPosition; this .onPageChangeListener.onPageSelection(targetPosition); } return true ; } @Override public void onScrollStateChanged ( final int state) { super .onScrollStateChanged(state); if (state == SCROLL_STATE_IDLE) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager(); int screenWidth = Resources.getSystem().getDisplayMetrics().widthPixels; int lastVisibleItemPosition = linearLayoutManager.findLastVisibleItemPosition(); View lastView = linearLayoutManager.findViewByPosition(lastVisibleItemPosition); int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition(); View firstView = linearLayoutManager.findViewByPosition(firstVisibleItemPosition); // distance we need to scroll int leftMargin = (screenWidth - lastView.getWidth()) / 2 ; int rightMargin = (screenWidth - firstView.getWidth()) / 2 + firstView.getWidth(); int leftEdge = lastView.getLeft(); int rightEdge = firstView.getRight(); int scrollDistanceLeft = leftEdge - leftMargin; int scrollDistanceRight = rightMargin - rightEdge; int targetPosition = - 1 ; if (leftEdge > screenWidth / 2 ) { smoothScrollBy(-scrollDistanceRight, 0 ); targetPosition = firstVisibleItemPosition+ 1 ; } else if (rightEdge < screenWidth / 2 ) { smoothScrollBy(scrollDistanceLeft, 0 ); targetPosition = lastVisibleItemPosition; } else { targetPosition = firstVisibleItemPosition; } if ( this .onPageChangeListener!= null ){ realPosition = targetPosition; this .onPageChangeListener.onPageSelection(targetPosition); } } } @Override public boolean handleMessage (Message msg) { int what = msg.what; switch (what){ case MSG_PLAY_NEXT: showNextPage(); break ; } return false ; } private void showNextPage () { if (!isPlaying){ return ; } if (!canRecyclePlaying()){ isPlaying = false ; return ; } Adapter adapter = getAdapter(); int currentItem = getCurrentItem(); if (adapter!= null && adapter.getItemCount()> 0 ) { if (currentItem == NO_POSITION ) { setCurrentItem( 0 ); } else { setCurrentItem(currentItem+ 1 ); } } mRecyclerHandler.sendEmptyMessageDelayed(MSG_PLAY_NEXT,TASK_TIMEOUT); } public void startPlay () { if (isPlaying){ stopPlay(); } if (!canRecyclePlaying()){ isPlaying = false ; return ; } isPlaying = true ; mRecyclerHandler.sendEmptyMessageDelayed(MSG_PLAY_NEXT,TASK_TIMEOUT); } @Override public void setAdapter (Adapter adapter) { super .setAdapter(adapter); if (canRecyclePlaying()){ if (realPosition==- 1 ){ realPosition = 1000 ; } setCurrentItem(realPosition, false ); } } private boolean canRecyclePlaying () { Adapter adapter = getAdapter(); if (adapter== null || adapter.getItemCount()< 1 ) return false ; return true ; } private void stopPlay () { isPlaying = false ; mRecyclerHandler.removeMessages(MSG_PLAY_NEXT); } @Override protected void onAttachedToWindow () { super .onAttachedToWindow(); if (lastIsPlayState){ startPlay(); } } @Override protected void onDetachedFromWindow () { super .onDetachedFromWindow(); lastIsPlayState = isPlaying; stopPlay(); } public static abstract class OnPageChangeListener extends RecyclerView . OnScrollListener { public abstract void onPageSelection ( int position) ; } }
Adapter+Holder 适配器
该类的作用主要用于约束和快速接入
public class QuickViewHolder extends RecyclerView.ViewHolder{ private SparseArray mViews; private View mConvertView; private QuickViewHolder(View v){ super(v); mConvertView = v; mViews = new SparseArray<>(); } public static QuickViewHolder get(ViewGroup parent, int layoutId){ View convertView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false); return new QuickViewHolder(convertView); } public T getView(int id){ View v = mViews.get(id); if(v == null){ v = mConvertView.findViewById(id); mViews.put(id, v); } return (T)v; } }
BannerAdapter实现,该类继承QuickAdapter
public class BannerAdapter extends QuickAdapter { public BannerAdapter(List datas) { super(datas); } @Override public int getLayoutId(int viewType) { return R.layout.item_banner_image; } @Override public void convert(QuickViewHolder holder, String url, int position) { final Resources resources = holder.itemView.getResources(); final int drawableId = resources.getIdentifier(url, "drawable", holder.itemView.getContext().getPackageName()); if(drawableId!=0) { ImageView bannerImage = holder.getView(R.id.banner_image_item); bannerImage.setImageResource(drawableId); } } }
三、使用
final RecyclerPagerView rpv = findViewById(R.id.recycler_pager); tipTextView = findViewById(R.id.tip_text); LinearLayoutManager lm = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false); rpv.setLayoutManager(lm); List imagelist = new ArrayList<>(); imagelist.add("banner_t1"); imagelist.add("banner_t2"); imagelist.add("banner_t3"); imagelist.add("banner_t4"); rpv.setAdapter(new BannerAdapter(imagelist)); rpv.setOnPageChangeListener(new PagerChangeListener(tipTextView,imagelist.size())); rpv.startPlay();
监听器
public static class PagerChangeListener extends RecyclerPagerView.OnPageChangeListener { private TextView tipTextView; private int size; public PagerChangeListener(TextView tipTextView,int size) { this.tipTextView = tipTextView; this.size = size; } @Override public void onPageSelection(int position) { tipTextView.setText((position%size+1)+"/"+size); } }
发表评论 取消回复