您的位置:首页 > 移动开发 > Android开发

关于 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:

@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 来完成上面的一系列动作,思路类似,也不继续分析了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  coordinate android