I. principle
ViewPager is a relatively high-frequency view component used in Android and handles events in the sliding process, so it is very suitable for the rotation chart. There are many ways to implement the round-robin graph. Horizontal View or RecylerView can also be used, but fling operations need to be handled. Here we use ViewPager to avoid these tasks.
There are many implementations of ViewPager's rotation on the Internet. Most of the principles give Pager Adapter's getCount an N-fold magnification, N greater than 100,000, etc. Here we use another idea, data mapping.
Data mapping scheme:
Assuming that the original data has N picture data, we construct a new data by putting the original first picture in the position of the new index N+1 and the original last picture in the first picture.
When the picture slides to position==0, we use viewPager.setCurrentItem(N,false), and when position=N+1, we use viewPager.setCurrentItem(1,false); we can actually slide infinitely.
Of course, the above scheme is feasible and simple, but it destroys the original data. We can use the algorithm to achieve the original data is not destroyed, and calculate the real data index.
private int getRealPosition(int position) { int realPosition = position; if(InnerWrapperPagerAdapter.this.getCount()>1){ if(position==0){ realPosition = (getCount()-2)-1; }else if(position==getCount()-1){ realPosition = 0; }else{ realPosition = position-1; } } return realPosition; }
2. Code Implementation
public class AutoBannerView extends ViewPager implements Runnable { private OnPagerChangeListener mOpageChangeListener; private long TIME_WAIT = 3000; //Rotation every 3 seconds private boolean isPlaying = false; private boolean lastStateIsPlaying = false; //Pre-rotation status public AutoBannerView(Context context) { this(context,null); } public AutoBannerView(Context context, AttributeSet attrs) { super(context, attrs); if(mOpageChangeListener==null){ mOpageChangeListener = new OnPagerChangeListener(this); } } public void startPlay(){ stopPlay(); isPlaying = true; postDelayed(this,TIME_WAIT); } private void stopPlay() { lastStateIsPlaying = isPlaying==true; isPlaying = false; removeCallbacks(this); } @Override public void setAdapter(PagerAdapter adapter) { if (null != getAdapter()) { removeOnPageChangeListener(mOpageChangeListener); } if (null == adapter) { super.setAdapter(adapter); } else { super.setAdapter(new InnerWrapperPagerAdapter(adapter)); addOnPageChangeListener(mOpageChangeListener); if(adapter.getCount()>1){ setCurrentItem(1,false); //GetCount > 1, which defaults to the 0th bit of the original data when initialized, which is the second bit of the new PagerAdapter } } } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()){ case MotionEvent.ACTION_DOWN: if(isPlaying){ stopPlay(); } break; case MotionEvent.ACTION_UP: if(lastStateIsPlaying){ startPlay(); } break; } return super.onTouchEvent(ev); } @Override public void run() { if(Looper.myLooper()!=Looper.getMainLooper()) return; //Require to work on the main thread if(null==getAdapter() || getAdapter().getCount()<=1) return; if(!isPlaying) return; //Stop execution int currentItem = this.getCurrentItem(); if(currentItem>0 && currentItem<getAdapter().getCount()-1){ setCurrentItem(currentItem+1,true); } postDelayed(this,TIME_WAIT); } /** * Packaging Pager Adapter to Realize Data Mapping */ private static class InnerWrapperPagerAdapter extends PagerAdapter{ private PagerAdapter mPagerAdapter; public InnerWrapperPagerAdapter(PagerAdapter pagerAdapter) { this.mPagerAdapter = pagerAdapter; } @Override public int getCount() { if(mPagerAdapter!=null) { //If the data is greater than 1, it means that it can be rotated. return mPagerAdapter.getCount() > 1 ? mPagerAdapter.getCount() + 2 : mPagerAdapter.getCount(); } return 0; } @Override public boolean isViewFromObject(View view, Object object) { if(mPagerAdapter!=null) { return mPagerAdapter.isViewFromObject(view, object); } return false; } @Override public Object instantiateItem(ViewGroup container, int position) { if(mPagerAdapter!=null) { int realPosition = getRealPosition(position); return mPagerAdapter.instantiateItem(container, realPosition); } return super.instantiateItem(container,position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { if(mPagerAdapter!=null) { int realPosition = getRealPosition(position); mPagerAdapter.destroyItem(container, realPosition, object); }else{ super.destroyItem(container,position,object); } } private int getRealPosition(int position) { int realPosition = position; if(InnerWrapperPagerAdapter.this.getCount()>1){ if(position==0){ realPosition = (getCount()-2)-1; }else if(position==getCount()-1){ realPosition = 0; }else{ realPosition = position-1; } } return realPosition; } @Override public int getItemPosition(Object object) { if(mPagerAdapter==null){ return mPagerAdapter.getItemPosition(object); } return super.getItemPosition(object); } @Override public void notifyDataSetChanged() { if(mPagerAdapter!=null) { mPagerAdapter.notifyDataSetChanged(); } else{ super.notifyDataSetChanged(); } } @Override public void registerDataSetObserver(DataSetObserver observer) { if(mPagerAdapter!=null) { mPagerAdapter.registerDataSetObserver(observer); }else{ super.registerDataSetObserver(observer); } } @Override public void unregisterDataSetObserver(DataSetObserver observer) { if(mPagerAdapter!=null) { mPagerAdapter.unregisterDataSetObserver(observer); }else{ super.unregisterDataSetObserver(observer); } } @Override public CharSequence getPageTitle(int position) { if(mPagerAdapter!=null) { int realPosition = getRealPosition(position); return mPagerAdapter.getPageTitle(realPosition); }else{ return super.getPageTitle(position); } } @Override public float getPageWidth(int position) { if(mPagerAdapter!=null){ int realPosition = getRealPosition(position); return mPagerAdapter.getPageWidth(realPosition); } return super.getPageWidth(position); } @Override public void startUpdate(ViewGroup container) { if(mPagerAdapter!=null) { mPagerAdapter.startUpdate(container); }else{ super.startUpdate(container); } } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { if(mPagerAdapter!=null) { int realPosition = getRealPosition(position); mPagerAdapter.setPrimaryItem(container, realPosition, object); }else{ super.setPrimaryItem(container,position,object); } } @Override public void finishUpdate(ViewGroup container) { if(mPagerAdapter!=null) { mPagerAdapter.finishUpdate(container); }else{ super.finishUpdate(container); } } @Override public Parcelable saveState() { if(mPagerAdapter!=null) { return mPagerAdapter.saveState(); } return super.saveState(); } @Override public void restoreState(Parcelable state, ClassLoader loader) { if(mPagerAdapter!=null) { mPagerAdapter.restoreState(state, loader); }else{ super.restoreState(state,loader); } } public void setPagerAdapter(PagerAdapter pagerAdapter) { this.mPagerAdapter = pagerAdapter; } } private static class OnPagerChangeListener implements ViewPager.OnPageChangeListener{ private final AutoBannerView mAutoBannerView; public OnPagerChangeListener(AutoBannerView autoBannerView) { this.mAutoBannerView = autoBannerView; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(shouldCancelHandle()) return; } @Override public void onPageSelected(int position) { if(shouldCancelHandle()) return; } @Override public void onPageScrollStateChanged(int state) { //In this method, the end-to-end switching is handled, and other methods may cause animation interruption problem. if(shouldCancelHandle()) return; if(state==SCROLL_STATE_IDLE){ //When the animation is finished, switch immediately int currentItem = mAutoBannerView.getCurrentItem(); if( (mAutoBannerView.getAdapter() instanceof InnerWrapperPagerAdapter) && mAutoBannerView.getAdapter().getCount()>1){ InnerWrapperPagerAdapter adapter = (InnerWrapperPagerAdapter) mAutoBannerView.getAdapter(); if(currentItem==0 ){ mAutoBannerView.setCurrentItem(adapter.getCount()-2,false); }else if(currentItem==adapter.getCount()-1){ mAutoBannerView.setCurrentItem(1,false); } } } } private boolean shouldCancelHandle() { return this.mAutoBannerView==null || mAutoBannerView.getAdapter()==null; } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if(lastStateIsPlaying){ //If it was previously rotated, continue to rotate startPlay(); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if(isPlaying) { stopPlay();// If you remove it from window s, stop rotating } } }