关于 NestedScrollView 和CoordinateLayout的交互 以及CoordinateLayout的分发
2016-03-25 14:45
453 查看
上一次说到,一般很少有behavior去重写behavior.onTouchEvent和behavior.onInterceptTouchEvent方法。那么其实我们可以直接忽略这一套流程,直接当他是正常的事件分发啦。
那么现在模拟这么一个情况,我们手指滑动NestedScrollView的项(设定其xml属性:app:layout_behavior=”@string/appbar_scrolling_view_behavior”),
然后AppbarLayout随之滚动。这是怎么发生的呢?
如果是正常的事件分发流程,我们会到达NestedScrollView的onInterceptTouchEvent 然后是onTouchEvent
onInterceptTouchEvent:
这个方法主要是判定是否要拦截垂直滚动的事件给自己消费,如果真的要拦截就返回true。 具体的内部判断就不粘贴了,因为不是今天的主角,大体上看过去,ACTION_MOVE 的话,如果超过一个y轴分量阈值那么就可以判定进入了滚动状态。ACTION_DOWN的话,如果当前滚动动画还没有结束(比如手指fling操作),那么现在按下去的话也算是一种拖动了(这里还不是很确定)
然后如果真的是上下拖动并在onInterceptTouchEvent 里头返回了true,事件流就跑到这个类的onTouchEvent方法内了。
onTouchEvent内部也是一个Switch case来分别处理不同类型的事件
自然的先从 ACTION_DOWN 开始
关键的地方出现了
好了主角来了startNestedScroll
跟进去
其实吧ViewGroup里头也有onStartNestedScroll和onNestedScrollAccepted方法,不过默认返回为false,而CoordinateLayout对他们进行了相应的重写。所以如果调用的父view返回false,那么
一种可能是这个父view不是CoordinateLayout,那么就继续沿着树向上找
另一个可能是这个父view是CoordinateLayout,但是这个父view的子view的behavior全都没有接受这个事件,那么也继续向上看看有没有更高层级的CoordinateLayout来接收
那么接下来就去CoordinateLayout看看实现
Coor依次调用了所有子view的behavior(如果有的话)的相应的onStartNestedScroll方法,并且返回是否至少一个behavior处理了这个事件
。
忽然想到了一个很好的比喻,这里Coor起了一个类似集线器的作用,各个子view相当于连在上头的电脑,他们整体构成一个星型拓扑的局域网结构。Coor负责转发某台电脑的请求给所有局域网内的连接。至于那台电脑要如何处理这个请求那就是电脑自己的事情。
继续看:
好了回到NestedScroll的onTouchEvent,刚才说完了ACTION_DOWN,现在自然是来看ACTION_MOVE 了
后面的套路其实和之前的onNestedScrollAccepted都差不多,同样也是会回调父view的Coor的相应的方法,然后Coor在相应方法里头遍历所有子view,首先检查子view的lp是否接受了这个事件流,如果是,就接着对子view的behavior进行相应的回调。就不罗嗦太多了
同理我们可以分析Recyclerview,在RecyclerView的onTouchEvent方法里头同样的也是用startNestedScroll 来完成上面的一系列动作,思路类似,也不继续分析了
那么现在模拟这么一个情况,我们手指滑动NestedScrollView的项(设定其xml属性:app:layout_behavior=”@string/appbar_scrolling_view_behavior”),
然后AppbarLayout随之滚动。这是怎么发生的呢?
如果是正常的事件分发流程,我们会到达NestedScrollView的onInterceptTouchEvent 然后是onTouchEvent
onInterceptTouchEvent:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getAction(); //mIsBeingDragged 关键标志位。如果已经进入滚动状态 if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { return true; } switch (action & MotionEventCompat.ACTION_MASK) { case...... ..............
这个方法主要是判定是否要拦截垂直滚动的事件给自己消费,如果真的要拦截就返回true。 具体的内部判断就不粘贴了,因为不是今天的主角,大体上看过去,ACTION_MOVE 的话,如果超过一个y轴分量阈值那么就可以判定进入了滚动状态。ACTION_DOWN的话,如果当前滚动动画还没有结束(比如手指fling操作),那么现在按下去的话也算是一种拖动了(这里还不是很确定)
然后如果真的是上下拖动并在onInterceptTouchEvent 里头返回了true,事件流就跑到这个类的onTouchEvent方法内了。
onTouchEvent内部也是一个Switch case来分别处理不同类型的事件
自然的先从 ACTION_DOWN 开始
关键的地方出现了
.... case MotionEvent.ACTION_DOWN: { .... startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); ..... } ....
好了主角来了startNestedScroll
@Override public boolean startNestedScroll(int axes) { return mChildHelper.startNestedScroll(axes); }
跟进去
public boolean startNestedScroll(int axes) { if (hasNestedScrollingParent()) { // Already in progress return true; } if (isNestedScrollingEnabled()) { ViewParent p = mView.getParent();//得到父view View child = mView; while (p != null) { if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {//回调父类的onStartNestedScroll方法! mNestedScrollingParent = p; ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);//回调父类的onNestedScrollAccepted方法! return true; } if (p instanceof View) { child = (View) p; } p = p.getParent();//如果回调失败,说明不是CoordinateLayout,那么继续向上找 } } return false; }
其实吧ViewGroup里头也有onStartNestedScroll和onNestedScrollAccepted方法,不过默认返回为false,而CoordinateLayout对他们进行了相应的重写。所以如果调用的父view返回false,那么
一种可能是这个父view不是CoordinateLayout,那么就继续沿着树向上找
另一个可能是这个父view是CoordinateLayout,但是这个父view的子view的behavior全都没有接受这个事件,那么也继续向上看看有没有更高层级的CoordinateLayout来接收
那么接下来就去CoordinateLayout看看实现
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { boolean handled = false; final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target, nestedScrollAxes); handled |= accepted; lp.acceptNestedScroll(accepted); //在子view的lp里头记录这个子view的behavior是否接受了事件!以后就靠着这个标志位来分发接下来的流程了! } else { lp.acceptNestedScroll(false); //记录子view的behaviior并没有接受这个事件 } } return handled; }
Coor依次调用了所有子view的behavior(如果有的话)的相应的onStartNestedScroll方法,并且返回是否至少一个behavior处理了这个事件
。
忽然想到了一个很好的比喻,这里Coor起了一个类似集线器的作用,各个子view相当于连在上头的电脑,他们整体构成一个星型拓扑的局域网结构。Coor负责转发某台电脑的请求给所有局域网内的连接。至于那台电脑要如何处理这个请求那就是电脑自己的事情。
继续看:
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) { mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes); mNestedScrollingDirectChild = child; mNestedScrollingTarget = target; final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); //如果在lp记录的是不接受事件,那直接continue跳过就好了 if (!lp.isNestedScrollAccepted()) { continue; } // 如果刚才lp记录的是接受 final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { //进一步调用子view的onNestedScrollAccepted viewBehavior.onNestedScrollAccepted(this, view, child, target, nestedScrollAxes); } } }
好了回到NestedScroll的onTouchEvent,刚才说完了ACTION_DOWN,现在自然是来看ACTION_MOVE 了
case MotionEvent.ACTION_MOVE:{ .... dispatchNestedPreScroll ... dispatchNestedScroll ... ... }
后面的套路其实和之前的onNestedScrollAccepted都差不多,同样也是会回调父view的Coor的相应的方法,然后Coor在相应方法里头遍历所有子view,首先检查子view的lp是否接受了这个事件流,如果是,就接着对子view的behavior进行相应的回调。就不罗嗦太多了
同理我们可以分析Recyclerview,在RecyclerView的onTouchEvent方法里头同样的也是用startNestedScroll 来完成上面的一系列动作,思路类似,也不继续分析了
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories