1.解决 ViewPager2 添加 android:overScrollMode="never" 属性无效问题
- 查阅源码得知 ViewPager2 既没有重写 setOverScrollMode() 方法也没有用到 View 的 getOverScrollMode() 方法
- ViewPager2 会展示出 RecyclerView 的特点是因为其继承于 ViewGroup 并填充了 (protected声明的) RecyclerView 对象导致
- ViewPage2 是 final 类
由上述分析可知,最简单的使 overScrollMode 属性生效的办法就是直接去设置 ViewPager2 中 RecyclerView 的 overScrollMode 属性,但 ViewPage2 并未暴露 RecyclerView 对象,且 ViewPager2 是 final 类无法重写。所以选择使用反射获取 RecyclerView 对象并设置 overScrollMode 属性。
ViewPager2 mVpMain;
private void init() {
RecyclerView recyclerView = (RecyclerView) getPrivateValue(mVpMain, "mRecyclerView");
if (recyclerView != null) {
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
}
}
/**
* 通过反射获取私有的成员变量
*
* @param object
* @return
*/
private Object getPrivateValue(Object object, String fieldName) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
// 参数值为true,打开禁用访问控制检查
//setAccessible(true) 并不是将方法的访问权限改成了public,而是取消java的权限控制检查。
//所以即使是public方法,其accessible 属相默认也是false
field.setAccessible(true);
return field.get(object);
} catch (Exception e) {
Log.e("Test", "e.getMessage() = " + e.getMessage());
return null;
}
}
2. ViewPager2 嵌套 RecyclerView 滑动冲突问题解决(两 View 滚动方向均为竖直滚动)
- Android 滑动冲突解决办法常用两种:外部拦截法和内部拦截法
- ViewPager2 是 final 类
外部拦截:父 View 根据需要对事件进行拦截,逻辑处理放在父 View 的 onInterceptTouchEvent 方法中。需要重写父 View 的 onInterceptTouchEvent 方法,并根据逻辑需要做相应的拦截
内部拦截:父 View 不拦截任何事件,所有事件都传递给子 View ,子 View 根据需要决定是自己消费事件还是给父 View 处理。子 View 需要重写 dispatchTouchEvent 方法,并根据逻辑需要通过 requestDisallowInterceptTouchEvent 方法拦截事件
因为 ViewPager2 为 final 类无法重写,所以选择内部拦截法来处理滑动冲突,重写 RecyclerView 。
/**
* Author : Ziwen Lan
* Date : 2019/10/30
* Time : 13:53
* Introduction : 处理与纵向滚动的父View的滑动冲突
*/
public class NestedRecyclerView extends RecyclerView {
private float mStartY;
public NestedRecyclerView(@NonNull Context context) {
super(context);
}
public NestedRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public NestedRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
mStartY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float endY = ev.getY();
if (endY > mStartY) {
//向上滑动
if (canScrollVertically(-1)) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
getParent().requestDisallowInterceptTouchEvent(false);
}
} else {
//向下滑动
if ((canScrollVertically(1))) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
getParent().requestDisallowInterceptTouchEvent(false);
}
}
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
}