Custom vertical sliding viewpager, infinite loop, timed switching

The main idea of customization is to inherit the viewpager, then rewrite the swapTouchEvent method to modify the handover gesture, instead of sliding up and down to trigger the handover event.

public class VerticalViewPager extends ViewPager {
    private boolean isPagingEnabled = true;
    public VerticalViewPager(Context context) {
        super(context);
    }

    public VerticalViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    private MotionEvent swapTouchEvent(MotionEvent event) {
        float width = getWidth();
        float height = getHeight();
        event.setLocation((event.getY() / height) * width, (event.getX() / width) * height);
        return event;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        return super.onInterceptTouchEvent(swapTouchEvent(MotionEvent.obtain(event)))&&this.isPagingEnabled;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return super.onTouchEvent(swapTouchEvent(MotionEvent.obtain(ev)))&&this.isPagingEnabled;
    }

    public void setPagingEnabled(boolean b) {
        this.isPagingEnabled = b;
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int height = 0;
        for(int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if(h > height) height = h;
        }

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }



}

The height of the viewpager is determined by the height of the largest sub-item, and the sliding can be set by setPagingEnabled. By default, sliding is allowed.

The above is just to set the switching gesture. The switching animation needs to be reset.

public class VerticalTransformer implements ViewPager.PageTransformer{

    @Override
    public void transformPage(View view, float position) {
        view.setTranslationX(view.getWidth() * -position);
        float yPosition = position * view.getHeight();
        view.setTranslationY(yPosition);
    }
}

Originally, it was switched in the x direction, but when set to - position, it canceled out. When switched in the y direction, it can set transparency, if necessary.

Set up

mVerticalViewPager.setPageTransformer(true, new VerticalTransformer());

Is it easy to complete a vertically switched viewpager with the above settings?

Here is the complete call

 private void initVerticalPager() {
        if (mVerticalViewPager == null) {
            mVerticalViewPager = (VerticalViewPager) getRootView().findViewById(R.id.vertical_viewpager);
            mVerticalPagerAdapter = new VerticalPagerAdapter();
            mVerticalViewPager.setAdapter(mVerticalPagerAdapter);
            mVerticalViewPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
            mVerticalViewPager.setPageTransformer(true, new VerticalTransformer());
        } else {
            mVerticalPagerAdapter.notifyDataSetChanged();
        }
}

Adapter

  private class VerticalPagerAdapter extends PagerAdapter {
        @Override
        public int getCount() {
            return Integer.MAX_VALUE;
        }

        @Override
        public boolean isViewFromObject(View view, Object o) {
            return view == o;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view = null;
            view = View.inflate(mContext, R.layout.weiget_main_information, null);
            switch(position%count){
            case 0:
                   view = ...;
                   break;
            case 1:
            ...
            }
            container.addView(view);
            return view;
        }
    }

We set up a pseudo-infinite loop viewpager here, so we put back the maximum value of the shaping in the getCount method. In fact, it is almost impossible for us to achieve this maximum value by sliding between app s. As for the loop, we instantiate the new view in instantiate Item according to the requirement, and instantiate the new view every time. And initialize the data without worrying about generating too much garbage, because the view is removed from the destroyItem method and waiting for gc to recycle


    private static int SWITCH_TIME = 30*1000;
    
    public void startSwitchTask() {

        if (handler == null) {
            handler = new Handler();
        }
        handler.removeCallbacksAndMessages(null);
        handler.postDelayed(runnable,SWITCH_TIME);
    }

    public void stopSwitchTask() {
        if (handler != null) {
            handler.removeCallbacksAndMessages(null);
            handler = null;
        }

    }

  private Runnable runnable = new Runnable( ) {
        public void run ( ) {
            doSwitch();
            handler.postDelayed(this,SWITCH_TIME);
        }
    };

    private void doSwitch() {
        if(mVerticalViewPager ==null){
            return;
        }
        mVerticalViewPager.setCurrentItem(mVerticalViewPager.getCurrentItem()+1);
    }

The ingenuity of timing is

Runnable is triggered by handler.postDelayed, then business is completed in runnable, and handler.postDelayed method is triggered. This method needs to be valid within the action declaration cycle because of the use of handler. If action is recycled, stopSwitchTask method needs to be invoked in onDestroy method to avoid memory leak.

The doSwitch method switches viewpager, position increases, and no additional variables need to be maintained.

Added by sagetim on Sat, 29 Jun 2019 21:44:27 +0300