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

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 fragments = new ArrayList<>();
    private List 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 fragments;

    public FragmentPager2Adapter(@NonNull FragmentActivity fragmentActivity, List 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 fragments = new ArrayList<>();
    private List 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次翻页)