如题,如果想做一个直播页面的垂直循环切换,在没有AndroidX的时候呢,是改一下官方的ViewPager,让它可垂直切换,如果想实现循环切换,可能会有白屏的情况。
现在有了AndroidX,强大的ViewPager2来了,可横向滑动,也可垂直滑动,只需设置
android:orientation="vertical"
属性,就可改变切换方向。
如果想实现ViewPager2的无限循环,以前有两个思路:
1、将viewpager上限设置成一个很大的数,Integer.MAX_VALUE 第一个页面设置到中间。然后滑动的时候,用当前的序号与viewpager页面数取余得到目标页面的序号,然后显示出来。理论上一个人不会无聊到一直左滑或者右滑。因此可以模拟无限循环。
2、假设viewpager中有四个页面,分别为A、B、C、D。然后在A左边添加一个页面D,在D右边添加一个页面A,变成 D、A、B、C、D、A。当滑到左侧D时跳转到A,滑到右侧A时跳转到D
既然我们用了ViewPager2,并且ViewPager2基于RecyclerView实现。这意味着RecyclerView的优点将会被ViewPager2所继承。
那么,我们可不可以在第2种思路上优化一下呢,下面直接上代码:
1、LivePlayerActivity 布局
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
2、LivePageAdapter 代码
public class LivePageAdapter extends FragmentStateAdapter {
private List<Fragment> fragments;
public LivePageAdapter2(@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();
}
}
3、在LivePlayerActivity中设置
fragmentList.clear();
for (LiveBean bean: liveBeanList){
PlayerFragment fragment = new PlayerFragment();
fragment.setData(bean);
fragmentList.add(fragment);
}
fragmentAdapter = new LivePageAdapter2(this, fragmentList);
//避免fragment 调用onCreate()
playerPager.setOffscreenPageLimit(fragmentList.size());
playerPager.setAdapter(fragmentAdapter);
4、无限滚动(重点来了!!!)
MyOnPageChangeCallback 代码
public class MyOnPageChangeCallback extends ViewPager2.OnPageChangeCallback {
private static final String TAG = "MyOnPageChangeCallback";
private ViewPager2 vp2;
// fragment的个数
private int fragmentSize;
//记录上一次滑动的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 fragmentSize) {
this.vp2 = vp2;
this.fragmentSize = fragmentSize;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
if (isScrolling) {
// MyLogger.i(TAG,">>滑动中>>>position:"+position+">>>positionOffset:"+positionOffset+">>>>positionOffsetPixels:"+positionOffsetPixels);
if (lastValue > positionOffsetPixels) {
// MyLogger.i(TAG,"// 递减,向左侧滑动、上");
turnToLeft = false;
} else if (lastValue < positionOffsetPixels) {
// MyLogger.i(TAG,"// 递增,向右侧滑动,下");
turnToLeft = true;
}
}
lastValue = positionOffsetPixels;
}
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
realPosition = position;
if (mState == ViewPager2.SCROLL_STATE_SETTLING) {
if (changeViewCallback != null && lastPosition != position) {
if (turnToLeft) {
virtualPosition++;
}
if (!turnToLeft) {
virtualPosition--;
}
changeViewCallback.changeView(turnToLeft, virtualPosition);
lastPosition = position;
}
turnToLeft = false;
// MyLogger.i(TAG, "滑动结束》》》》lastPosition:" + lastPosition + "virtualPosition:" + position);
}
}
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
isScrolling = state == ViewPager.SCROLL_STATE_DRAGGING;
this.mState = state;
if (state == ViewPager2.SCROLL_STATE_IDLE) {
// 小于2个没有必要翻页
if (fragmentSize < 2) {
return;
}
// MyLogger.i(TAG, "当前真实下标:" + realPosition + "virtualPosition:" + virtualPosition);
// 没有做无限滚动哦,只能从 1页翻到 n页
if (virtualPosition < 0 || virtualPosition > fragmentSize - 1) {
return;
}
//当前页码判断可知道是第一页还是最后一页
if (realPosition == fragmentSize - 1 && virtualPosition == fragmentSize - 1) {
//最后一页-》切换为第一页
vp2.setCurrentItem(0, false);
realPosition = 0;
} else if (realPosition == 0 && virtualPosition == 0) {
//第一页-》切换为最后一页
vp2.setCurrentItem(fragmentSize - 1, false);
realPosition = fragmentSize - 1;
}
virtualPosition = realPosition;
}
}
/**
* 滑动状态改变回调
*/
public interface ChangeViewCallback {
/**
* 切换视图 ?决定于left和right 。
*
* @param turnToLeft
* @param virtualPosition
*/
public void changeView(boolean turnToLeft, int virtualPosition);
}
public void setChangeViewCallback(ChangeViewCallback callback) {
changeViewCallback = callback;
}
}
5、要想实现无限滚动,只需在LivePlayerActivity 中设置
//无限滚动
MyOnPageChangeCallback callback = new MyOnPageChangeCallback(playerPager,fragmentList.size());
playerPager.registerOnPageChangeCallback(callback);
6、当然,我们在MyOnPageChangeCallback中写了切换监听,你也可以用以下方式设置数据
callback.setChangeViewCallback((turnToLeft, virtualPosition) -> {
// datas 通过virtualPosition 拿到对于的数据 再传给 fragment
LiveBean bean = liveBeanList.get(virtualPosition);
for (Fragment fragment : fragmentList) {
PlayerFragment fragment1 = (PlayerFragment) fragment;
fragment1.setData(bean);
}
});
好了,自此垂直的无限切换就完成了。
当然,有个小问题,就是当切换到最后一个fragment后,再次滑动,切换回第一个fragment的时候,页面一下就跳转到第一个fragment,感觉上不大好,这个可以设置自定义动画来优化。
大家如果有任何疑问和不正确的地方,欢迎指正!!!
谢谢大家!!!
最后感谢大佬提供的资料: