1. 程式人生 > >Android 自定義實現輪播圖實踐

Android 自定義實現輪播圖實踐

開發十年,就只剩下這套架構體系了! >>>   

一、原理

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中移除掉,停止輪播
        }
    }
}