Android 自定義實現輪播圖實踐
阿新 • • 發佈:2019-03-24
一、原理
ViewPager是Android中使用頻率相對較高的view元件,同時對滑動過程中的事件進行了處理,因此非常適合輪播圖。關於輪播圖的實現,有很多方法,使用HorizontalView或者RecylerView也可以實現,但是需要處理fling操作,這裡我們用ViewPager避免這些工作。
網上有很多關於ViewPager輪播的輪播實現,其原理大多數給PagerAdapter的getCount 放大N倍,N大於100,1000等。這裡我們使用另一種思路,資料對映。
資料對映方案:
假設原始資料有N張圖片資料,我們將資料N+2,然後將原來的第一張圖片放到新的 索引為N+1的位置,原來的最後一張圖片放到第一張圖片,這樣構建了一個新的資料。
當圖片滑動到 position==0 時,我們使用viewPager.setCurrentItem(N,false) ,當position=N+1時,我們使用viewPager.setCurrentItem(1,false);便可實無限滑動。
當然,以上方案可行,也很簡單,但是卻破壞了原始資料,我們可以通過演算法實現原始資料不被破壞的實現方式,計算出真實的資料索引。
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; }
二、程式碼實現
public class AutoBannerView extends ViewPager implements Runnable { private OnPagerChangeListener mOpageChangeListener; private long TIME_WAIT = 3000; //每3秒輪播一次 private boolean isPlaying = false; private boolean lastStateIsPlaying = false; //停止輪播之前的狀態 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 ,初始化時預設顯示原始資料的第0位,也就是新的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; //要求在主執行緒工作 if(null==getAdapter() || getAdapter().getCount()<=1) return; if(!isPlaying) return; //停止執行 int currentItem = this.getCurrentItem(); if(currentItem>0 && currentItem<getAdapter().getCount()-1){ setCurrentItem(currentItem+1,true); } postDelayed(this,TIME_WAIT); } /** * 包裝PagerAdapter,實現資料對映 */ private static class InnerWrapperPagerAdapter extends PagerAdapter{ private PagerAdapter mPagerAdapter; public InnerWrapperPagerAdapter(PagerAdapter pagerAdapter) { this.mPagerAdapter = pagerAdapter; } @Override public int getCount() { if(mPagerAdapter!=null) { //如果資料大於1,說明可以輪播 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) { //在該方法中處理首尾切換,其他方法會產生動畫中斷問題 if(shouldCancelHandle()) return; if(state==SCROLL_STATE_IDLE){ //當動畫執行完,立即切換 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){ //如果之前是輪播的,那麼繼續輪播 startPlay(); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if(isPlaying) { stopPlay();// 如果從window中移除掉,停止輪播 } } }