ViewPager2一页展示多个Item且两边滑动
研究背景
在项目中有一个滚轮选择器的需求,对于小组件的研发,我一直倾向于自研,一方面功能不复杂,另一方面也锻炼自己的思维能力。不只是局限于上次研发。
传统Viewpager解决方法
对于传统的Viewpager的解决方法是
<androidx.viewpager.widget.ViewPager
android:layout_width="match_parent"
android:clipToPadding="false"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:gravity="center_horizontal"
android:layout_height="300dp"/>
这个就可以了。
Viewpager2解决方法
布局上不能截断子类绘制
<androidx.viewpager2.widget.ViewPager2
android:layout_width="match_parent"
android:layout_height="135dp"
android:clipChildren="false"
android:orientation="vertical" />
代码上,需要捞出里面真正实现的RecyclerView
RecyclerView recyclerView = (RecyclerView) mPager.getChildAt(0);
int padding = ScreenUtil.dip2px(45);
recyclerView.setPadding(0, padding, 0, padding);
recyclerView.setClipToPadding(false);
原理分析
Viewpager
如果我们查看源码,在Viewpager中
public class ViewPager extends ViewGroup {
@Override
public boolean onTouchEvent(MotionEvent ev) {
//做点击事件处理
}
}
自己来实现所有Viewpager
的功能,代码量巨大。我们的每个Item都是Viewpager
的childView
。
所以,我们做padding
还有不截断,都是直接作用在childView
上的。
Viewpager2
但是,在Viewpager2上是不一样的。
public final class ViewPager2 extends ViewGroup {
//Java中默认私有的,所以直接拿不到
RecyclerView mRecyclerView;
//每个构造函数,都做了相同操作
public ViewPager2(@NonNull Context context) {
super(context);
initialize(context, null);
}
//初始化方法
private void initialize(Context context, AttributeSet attrs) {
//已省略无用代码
mRecyclerView = new RecyclerViewImpl(context);
mLayoutManager = new LinearLayoutManagerImpl(context);
mRecyclerView.setLayoutManager(mLayoutManager);
setOrientation(context, attrs);
mRecyclerView.setLayoutParams(
new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams());
}
//源码注释翻译
//稍微修改了RecyclerView,以获得ViewPager行为在可访问性和启用/禁用用户滚动。
private class RecyclerViewImpl extends RecyclerView {
RecyclerViewImpl(@NonNull Context context) {
super(context);
}
@RequiresApi(23)
@Override
public CharSequence getAccessibilityClassName() {
if (mAccessibilityProvider.handlesRvGetAccessibilityClassName()) {
return mAccessibilityProvider.onRvGetAccessibilityClassName();
}
return super.getAccessibilityClassName();
}
@Override
public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setFromIndex(mCurrentItem);
event.setToIndex(mCurrentItem);
mAccessibilityProvider.onRvInitializeAccessibilityEvent(event);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
return isUserInputEnabled() && super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isUserInputEnabled() && super.onInterceptTouchEvent(ev);
}
}
}
我们不用去分析里面怎么做的处理。其实很简单就能看出他是一种什么样的设计。
- 在Viewpager2中,对
RecyclerView
进行了包装。RecyclerViewImpl
就是一个包装类。
- 真正实现滑动行为的是里面的
RecyclerView
。而Viewpager2
可以认为是另一个包装类,也是另一个容器。
所以,我们针对Viewpager2
的解决方法就很好理解了。拿到里面真正盛放childView
的RecyclerView
。再进行padding
和clip
的设置。
总结
viewpager2
的设计内部采用了RecyclerView
,这个官方文档说的很清楚。但是,是采用包装的方式。优点在于有了RecyclerView很好用的复用机制。但是,无形中增加了一个页面层级。(这个缺点在优点面前,确实不算什么。但是需要知道。)