Practice of Android Custom Implementation of Rotary Graph

Keywords: Mobile Android

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
        }
    }
}

Posted by DeGauss on Tue, 26 Mar 2019 03:09:32 -0700