一、需求 之前一篇博客使用ViewPager实现轮播图《Android ViewPager实现循环轮播图》,但是ViewPager有个天生的缺陷是View无法重用,此外ViewPager的滑动过程会频
一、需求
之前一篇博客使用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 onTouchEvent(MotionEvent e) { int action = e.getAction(); if(action==MotionEvent.ACTION_DOWN){ lastIsPlayState = isPlaying; if(lastIsPlayState){ stopPlay(); } }else if(action==MotionEvent.ACTION_UP || action==MotionEvent.ACTION_CANCEL){ if(lastIsPlayState){ startPlay(); } } return super.onTouchEvent(e); } @Override public boolean fling(int velocityX, int velocityY) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) getLayoutManager(); int screenWidth = getWidth(); // 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 = getWidth(); 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; if(adapter.getItemCount()==3000){ realPosition = realPosition - 1; } } 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<View> 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 extends View> 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 static abstract class BaseQuickAdapter<VH extends QuickViewHolder,DM extends Serializable> extends RecyclerPagerView.Adapter{ private List<DM> dataset; public BaseQuickAdapter(List<DM> dataset){ this.dataset = dataset; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return QuickViewHolder.get(parent,getLayoutId(viewType)); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { convert((VH)holder,getItem(position),position); } public abstract void convert(VH holder, DM dataModel, int position); public abstract int getLayoutId(int viewType); public DM getItem(int position) { if(dataset==null) return null; if(position<0 || position>=getItemCount()) { return null; } return dataset.get(position%dataset.size()); } @Override public int getItemCount() { if(dataset==null || dataset.isEmpty()) return 0; return dataset.size()*1000; } } public static class BannerAdapter extends BaseQuickAdapter { public BannerAdapter(List<Item> dataset) { super(dataset); } @Override public void convert(QuickViewHolder holder, Serializable dataModel, int position) { if(!(dataModel instanceof Item)) return; TextView textView = holder.getView(R.id.text); textView.setText(((Item) dataModel).getTitle()); ImageView imageView = holder.getView(R.id.image); imageView.setImageDrawable(((Item) dataModel).getImageResource()); } @Override public int getLayoutId(int viewType) { return R.layout.item_banner; } }
三、使用
监听器
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); } }
数据集
public static class Item implements Serializable { Drawable drawable; String title; public Item(int color, String s) { this.drawable = new ColorDrawable(color); this.title = s; } public Drawable getImageResource() { return drawable; } public String getTitle() { return title; } public static List<Item> getSampleData(){ int[] colors = { 0xffDC143C, 0xff00BFFF, 0xffA52A2A }; List<Item> lst = new ArrayList<>(); for (int i=0;i<colors.length;i++) { Item item = new Item(colors[i%colors.length],"第"+(i%colors.length+1)+"张图"); lst.add(item); } return lst; } }