NestedScrollView RecycleView 嵌套 滑动辩论
场景形貌
效果演示
实现思绪
标题和优化
优化
参考文档
场景形貌
利用NestedScrollView 内嵌RecycleView时,当用户上滑时,NestedScrollView必要起首相应上滑变乱,直到ScrollView无法滑动,再由RecycleView举行相应滑动变乱
效果演示
实现思绪
参考谷歌开辟者文档中关于view group变乱分发的思绪,自界说CustomScrollView继续 NestedScrollView 重写onInterceptTouchEvent方法,在拦截到上滑变乱时,判定当前的scrollerView是否已经滑动到顶部;
如果ScrollView没有处于顶部,返回true,代表处置惩罚并斲丧后续一系列触摸变乱,包罗一系列的Action Move获取到的point到Action Up变乱,这中心的变乱值都交给 ScrollView处置惩罚滑动;
如果ScrollView已经处于顶部,不再拦截上滑变乱,会由NestedScrollView默认分发给子控件RecycleView举行处置惩罚
·```
public class CustomScrollView extends NestedScrollView {
public CustomScrollView(@NonNull Context context) { super(context);}private float maxSlideDis;//向上滑动的最大滑动间隔,没有凌驾这个间隔时,拦截并处置惩罚掉向上滑动的变乱//在activity或fragment中,根据布局参数举行设置private float mDownY;private float mSlop;public CustomScrollView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); mSlop = ViewConfiguration.get(context).getScaledTouchSlop();}public void setMaxSlideDis(float maxSlideDis) { this.maxSlideDis = maxSlideDis;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { Logger.log("CustomScrollView onInterceptTouchEvent " + ev.getAction()); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownY = ev.getRawY(); break; case MotionEvent.ACTION_MOVE: float dis = ev.getRawY() - mDownY; if (dis < 0 && Math.abs(dis) >= mSlop) { //当触摸变乱是向上滑动而且滑动间隔凌驾屏幕的最小滑动单位时 return needScrollParent(); } break; } return super.onInterceptTouchEvent(ev);}//scroller 是否已经滑动到了最高点public boolean needScrollParent() { Logger.log("CustomScrollView maxSlideDis = " + maxSlideDis + " getScrollY =" + getScrollY()); return getScrollY() < maxSlideDis;}public CustomScrollView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);}}
标题和优化到这一步已经实现先滑动scrollerView再滑动recycleView,但是存在一个标题,当上滑scrollerView的过程中,当scroll view滑动到最顶部不放手指,recycleView不会开始向上滚动,必须等手指离开屏幕,再重新上滑recycle view才可以让其滚动,就像上文gif所展示的;要明白这个标题标缘故原由,起首要搞清楚一个变乱系列 和 view分发机制流程变乱系列: 从ACTION_DOWN 到ACTION_UP 中包罗的全部变乱,包罗此中的全部的ACTION_MOVE变乱,为一个变乱系列对view变乱分发机制不熟悉的同砚可以看下这个分发机制伪代码public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if(onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
onInterceptTouchEvent方法要注意: 吸收到某一个变乱系列中的变乱值后,如果返回false大概true,那么后续都不再回调这个方法,这个true或false所代表的状态会连续到这个变乱系列竣事因此scroller view的onInterceptTouchEvent 返回true拦截了ACTION_MOVE的前几个变乱值后,该变乱系列的后续变乱值也会直接被scroller view的onTouch斲丧掉, 而且onInterceptTouchEvent不再见收到该变乱系列的后续ev值的回调,由于在dispatchTouchEvent中,直接将后续变乱给到了onTouch举行斲丧;导致后续的滑动变乱不绝被scrollerview所斲丧,而我们想要下面如许丝滑的滑动体验,就要将scroller view滑动到顶部后的后续变乱值分发给子视图举行处置惩罚:在一次滑动变乱中完成从scroller view到recycleveiw的滑动,手指不离开屏幕优化鉴于上述标题,我们把变乱的“拦截”放到scroller的dispatchTouchEvent方法中;由于view分发机制起首将每个变乱值都传入到dispatchTouchEvent,并在此中通过onInterceptTouchEvent 的返回值来决定是否将该变乱分发给子视图的dispatchTouchEvent;如果scroller view必要滑动,就直接调用onTouch举行斲丧变乱,如果scroller view不必要滑动了,就让dispatchTouchEvent默认调用到onInterceptTouchEvent来判定后续变乱,这里onInterceptTouchEvent中判定如果scroller view不必要滑动,直接返回false,name后续的ACTION_MOVE值都会被分发给子veiw处置惩罚代码:@Override
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Logger.log("CustomScrollView dispatchTouchEvent ACTION " + ev.getAction());
switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownY = ev.getRawY(); break; case MotionEvent.ACTION_MOVE: float dis = ev.getRawY() - mDownY; if (dis < 0 && Math.abs(dis) >= mSlop) { //当触摸变乱是向上滑动而且滑动间隔凌驾屏幕的最小滑动单位时 if (needScrollParent()) { onTouchEvent(ev); Logger.log("dispatchTouchEvent 必要拦截"); return true; } else { Logger.log("dispatchTouchEvent 不必要拦截"); //这里返回 super.dispatchTouchEvent(ev); 在super.dispatchTouchEvent(ev)中会调用下面的onInterceptTouchEvent 方法来判定 } } break; } return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { Logger.log("CustomScrollView onInterceptTouchEvent " + ev.getAction()); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownY = ev.getRawY(); break; case MotionEvent.ACTION_MOVE: float dis = ev.getRawY() - mDownY; if (dis < 0 && Math.abs(dis) >= mSlop) { //当触摸变乱是向上滑动而且滑动间隔凌驾屏幕的最小滑动单位时,不拦截滑动变乱 //这里的目标是取消dispatchTouchEvent中对该变乱系列的拦截,当scroller view不必要拦截时, //会实验到这里, return false; } break; } return super.onInterceptTouchEvent(ev);} |