美文网首页
4个fragment实现viewpager2的1-N次翻页

4个fragment实现viewpager2的1-N次翻页

作者: 眼中有码 | 来源:发表于2021-04-24 18:12 被阅读0次

    1.引言

    viewpager2是JetPack组件库推出的组件之一,是原生viewPager的升级但是不兼容,因为ViewPager2的Adapter 继承于RecycleView.Adpater。

    ViewPager+Fragment实现无限滚动的方案主要有2种

    • 方案一:将viewpager上限设置成一个很大的数,第一个页面设置到中间。然后滑动的时候,用当前的序号与viewpager页面数取余得到目标页面的序号,然后显示出来。理论上一个人不会无聊到一直左滑或者右滑。因此可以模拟无限循环。

    • 方案二:假设viewpager中有四个页面,分别为A、B、C、D。然后在A左边添加一个页面D,在D右边添加一个页面A,变成 D、A、B、C、D、A。当滑到D时跳转到D,滑到A时跳转到A

    本方案使用的是ViewPager2+Fragment 加 方案二 的实现方式。

    2. 方案实现

    1. 无限的实现

    public class ViewActivity extends AppCompatActivity {
    
        private ViewPager2 vp2;
        private List<Fragment> fragments = new ArrayList<>();
        private List<String> datas = new ArrayList<>();
     
    
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_view);
            vp2 = (ViewPager2) findViewById(R.id.vp2);
    
            fragments.add(ItemFragment.newInstance("1"));
            fragments.add(ItemFragment.newInstance("2"));
            fragments.add(ItemFragment.newInstance("3"));
            fragments.add(ItemFragment.newInstance("4"));
    
            vp2.setOffscreenPageLimit(2);
            vp2.setAdapter(new FragmentPager2Adapter(this, fragments));
            MyOnPageChangeCallback callback = new MyOnPageChangeCallback(vp2);
            vp2.registerOnPageChangeCallback(callback);
        }
    
    }
    
    
    public class FragmentPager2Adapter extends FragmentStateAdapter {
    
    
        private final List<Fragment> fragments;
    
        public FragmentPager2Adapter(@NonNull FragmentActivity fragmentActivity, List<Fragment> fragments) {
            super(fragmentActivity);
            this.fragments = fragments;
        }
    
        @NonNull
        @Override
        public Fragment createFragment(int position) {
            return fragments.get(position);
        }
    
        @Override
        public int getItemCount() {
            return fragments.size();
        }
    }
    
    public class MyOnPageChangeCallback1 extends ViewPager2.OnPageChangeCallback {
    
        private static final String TAG = "MyOnPageChangeCallback";
        private final ViewPager2 vp2;
        private final int fragmentSize;
        private int realPosition = 0;
    
    
        public MyOnPageChangeCallback1(ViewPager2 vp2, int fragmentSize) {
            this.vp2 = vp2;
            this.fragmentSize = fragmentSize;
        }
    
    
        @Override
        public void onPageScrollStateChanged(int state) {
            super.onPageScrollStateChanged(state);
            if (state == ViewPager2.SCROLL_STATE_IDLE) {
                if (realPosition == (fragmentSize - 1)) {
                    Log.i(TAG, "最后一页:" + 0);
                    vp2.setCurrentItem(1, false);
                } else if (realPosition == 0) {
                    Log.i(TAG, "第一页:" + (fragmentSize - 1));
                    vp2.setCurrentItem(fragmentSize - 2, false);
                }
             }
         
        }
    
        @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);
            realPosition = position;
        }
        
    }
    

    2. 如何获取真实的position和滑动的虚拟position

    1. 获取真实的position简单
     @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);
            realPosition = position;
        }
    
    2.难点在于如何滑动的虚拟position

    实现思路:根据滑动方向+ 是否翻过页 来判断是向左还是向右翻页了。

    public class MyOnPageChangeCallback2 extends ViewPager2.OnPageChangeCallback {
    
        private static final String TAG = "MyOnPageChangeCallback";
    
        //记录上一次滑动的positionOffsetPixels值
        private int lastValue = -1;
        // 滑动方向
        private boolean turnToLeft = false;
        private boolean isScrolling = false;
        private int mState;
        private ChangeViewCallback changeViewCallback;
        private int lastPosition = -1;
        private int virtualPosition = 0;
    
    
    
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels);
            if (isScrolling) {
                if (lastValue > positionOffsetPixels) {
                    // 递减,向右侧滑动
                    turnToLeft = false;
                } else if (lastValue < positionOffsetPixels) {
                    // 递减,向右侧滑动
                    turnToLeft = true;
                }
            }
            lastValue = positionOffsetPixels;
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
            super.onPageScrollStateChanged(state);
            isScrolling = state == ViewPager.SCROLL_STATE_DRAGGING;
            this.mState = state;
        }
    
        @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);
            if (mState == ViewPager2.SCROLL_STATE_SETTLING) {
                Log.i(TAG, "lastPosition:" + lastPosition + ",currentPosition:" + position);
                if (changeViewCallback != null && lastPosition != position) {
                    if (turnToLeft) {
                        virtualPosition++;
                    }
                    if (!turnToLeft) {
                        virtualPosition--;
                    }
                    changeViewCallback.changeView(turnToLeft, virtualPosition);
                    lastPosition = position;
                }
                turnToLeft = false;
            }
        }
    
    
        /**
         * 滑动状态改变回调
         *
         * @author zxy
         */
        public interface ChangeViewCallback {
            /**
             * 切换视图 ?决定于left和right 。
             *
             * @param turnToLeft
             * @param virtualPosition
             */
            public void changeView(boolean turnToLeft, int virtualPosition);
        }
        
        public void setChangeViewCallback(ChangeViewCallback callback) {
            changeViewCallback = callback;
        }
    }
    
    
    3. n 滚动 + 获取滑动的虚拟position

    如果实现无限滚动 那虚拟position 就没有任何意义了,所有限定了从第1页翻到n页。

    public class MyOnPageChangeCallback extends ViewPager2.OnPageChangeCallback {
    
        private static final String TAG = "MyOnPageChangeCallback";
        private ViewPager2 vp2;
        // fragment的个数 4个滑动效果最佳
        private int fragmentSize;
       // 真正数据个数 也就滑动的次数
        private int dataSize;
        //记录上一次滑动的positionOffsetPixels值
        private int lastValue = -1;
        // 滑动方向
        private boolean turnToLeft = false;
        private boolean isScrolling = false;
        private ChangeViewCallback changeViewCallback;
        private int lastPosition = -1;
        private int mState;
        private int realPosition = 0;
        private int virtualPosition = 0;
    
    
        public MyOnPageChangeCallback(ViewPager2 vp2, int dataSize, int fragmentSize) {
            this.vp2 = vp2;
            this.fragmentSize = fragmentSize;
            this.dataSize = dataSize;
        }
    
    
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels);
            if (isScrolling) {
                if (lastValue > positionOffsetPixels) {
                    // 递减,向右侧滑动
                    turnToLeft = false;
                } else if (lastValue < positionOffsetPixels) {
                    // 递减,向右侧滑动
                    turnToLeft = true;
                }
            }
            lastValue = positionOffsetPixels;
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
            super.onPageScrollStateChanged(state);
            isScrolling = state == ViewPager.SCROLL_STATE_DRAGGING;
            this.mState = state;
            if (state == ViewPager2.SCROLL_STATE_IDLE) {
               
              // 没有做无限滚动哦,只能从 1页翻到 n页
               if (virtualPosition <= 0 || virtualPosition >= dataSize - 1) {
                   return;
               }
                
                // 小于4 个没有必要,4个fragment  翻页效果最好
                if (fragmentSize < 4) {
                    return;
                }
                Log.i(TAG, "realPosition:" + realPosition + ",fragments.size():" + (fragmentSize - 1));
                //此处为你需要的情况,再加入当前页码判断可知道是第一页还是最后一页
                if (realPosition == (fragmentSize - 1)) {
                    Log.i(TAG, "最后一页:" + 0);
                    vp2.setCurrentItem(1, false);
                } else if (realPosition == 0) {
                    Log.i(TAG, "第一页:" + (fragmentSize - 1));
                    vp2.setCurrentItem(fragmentSize - 2, false);
                }
            }
        }
    
        @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);
            realPosition = position;
            if (mState == ViewPager2.SCROLL_STATE_SETTLING) {
                Log.i(TAG, "lastPosition:" + lastPosition + ",currentPosition:" + position);
                if (changeViewCallback != null && lastPosition != position) {
                    if (turnToLeft) {
                        virtualPosition++;
                    }
                    if (!turnToLeft) {
                        virtualPosition--;
                    }
                    changeViewCallback.changeView(turnToLeft, virtualPosition);
                    lastPosition = position;
                }
                turnToLeft = false;
            }
        }
    
    
        /**
         * 滑动状态改变回调
         *
         * @author zxy
         */
        public interface ChangeViewCallback {
            /**
             * 切换视图 ?决定于left和right 。
             *
             * @param turnToLeft
             * @param virtualPosition
             */
            public void changeView(boolean turnToLeft, int virtualPosition);
        }
    
        /**
         * set ...
         *
         * @param callback
         */
        public void setChangeViewCallback(ChangeViewCallback callback) {
            changeViewCallback = callback;
        }
    }
    
    
    public class ViewActivity extends AppCompatActivity {
    
        private ViewPager2 vp2;
        private List<Fragment> fragments = new ArrayList<>();
        private List<String> datas = new ArrayList<>();
    
    
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_view);
            vp2 = (ViewPager2) findViewById(R.id.vp2);
            for (int i = 0; i < 30; i++) {
                datas.add("数据:" + (i+1));
            }
    
            fragments.add(ItemFragment.newInstance("1"));
            fragments.add(ItemFragment.newInstance("2"));
            fragments.add(ItemFragment.newInstance("3"));
            fragments.add(ItemFragment.newInstance("4"));
            vp2.setOffscreenPageLimit(2);
            vp2.setAdapter(new FragmentPager2Adapter(this, fragments));
            MyOnPageChangeCallback callback = new MyOnPageChangeCallback(vp2, 30, 4);
            callback.setChangeViewCallback((turnToLeft, virtualPosition) -> {
              
                // datas 通过virtualPosition   拿到对于的数据 再传给 fragment
                String bean = datas.get(virtualPosition);
    
                for (Fragment fragment : fragments) {
                    ItemFragment fragment1 = (ItemFragment) fragment;
                    fragment1.setData(bean);
                }
            });
            vp2.registerOnPageChangeCallback(callback);
    
        }
    }
    
    

    3. 其他

    在实际开发过程中遇见过ViewPager2+Fragment使用FragmentStateAdapter时, Fragment数量变动,需要刷新的时候 notifyDataSetChanged()无效。看了下FragmentStateAdapter源码研究后找到解决之道。
    需要重写

    @Override
    public long getItemId(int position) {
         
    }
    @Override
    public boolean containsItem(long itemId) {
       
    }
    

    其源码中 getItemId 使用的是position,fragment复用导致数据错乱。可以给fragment绑定一个唯一Id。

    相关文章

      网友评论

          本文标题:4个fragment实现viewpager2的1-N次翻页

          本文链接:https://www.haomeiwen.com/subject/bjjlrltx.html