文章目录
  1. 1. 概述:
    1. 1.1. 前几天看到这样一个效果
    2. 1.2. 很想知道它的实现原理,于是看了一下源码,学到了一个新的东西:嵌套滑动
  2. 2. 背景
    1. 2.1. 在传统的View的事件分发机制中,事件一级一级的传递下去,直到被感兴趣的View拦截并消耗处理,如果所有的View都不拦截处理,那么最后一个View将会抛弃该事件。即:一个事件如果传给了子View那么父View将不可能再得到该事件。
  3. 3. 类:
    1. 3.1. 为了实现嵌套循环,5.0中提供了两个接口两个类:
  4. 4. 主要方法
  5. 5. 函数的回调关系如下:
    1. 5.1. 左边的子View回调右边的父View的方法
  6. 6. 参考Demo:

概述:

前几天看到这样一个效果

http://7xqumq.com1.z0.glb.clouddn.com/duwei_nestedscroll.gif

很想知道它的实现原理,于是看了一下源码,学到了一个新的东西:嵌套滑动

背景

在传统的View的事件分发机制中,事件一级一级的传递下去,直到被感兴趣的View拦截并消耗处理,如果所有的View都不拦截处理,那么最后一个View将会抛弃该事件。即:一个事件如果传给了子View那么父View将不可能再得到该事件。

类:

为了实现嵌套循环,5.0中提供了两个接口两个类:

1
2
3
4
5
6
7
8
9
10
11
//子View需实现的接口
NestedScrollingChild

//父View实现的接口
NestedScrollingParent

//子View的帮助类
NestedScrollingChildHelper

//父View 的帮助类
NestedScrollingParentHelper

主要方法

1
2
3
4
5
6
7
8
9
10
11
12
13

/**
* @param 水平滚动的像素值
* @param 垂直滚动的距离像素值
* @param consumed[0] :dx消耗掉的部分
* consumed[1] dy消费的部分
* @param offsetInWindow Optional. If not null, on return this will contain the offset
* in local view coordinates of this view from before this operation
* to after it completes. View implementations may use this to adjust
* expected input coordinate tracking.
* @return 如果父view消费了一些或全部,则返回true
*/

public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
ox = event.getX();
oy = event.getY();
//开始滑动!!!!!!!!!!
startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL);
break;
case MotionEvent.ACTION_MOVE://在MOVE中
//结束点
float clampedX = event.getX();
float clampedY = event.getY();
//移动的距离
int dx = (int) (clampedX - ox);
int dy = (int) (clampedY - oy);
//分发触屏事件给父类处理
if (dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)) {
//减掉父类消耗的距离
dx -= consumed[0];//consumed[0] will contain the consumed component of dx
dy -= consumed[1];//consumed[1] the consumed dy
}
offsetLeftAndRight(dx);//Offset this view's horizontal location by the specified amount of pixels
offsetTopAndBottom(dy);//通过指定像素来弥补控件的垂直位置
break;
case MotionEvent.ACTION_UP:
stopNestedScroll();//结束滑动!!!!!!!!
break;
}
return true;/*super.onTouchEvent(event);*/
}
1
2
3
4
5
6
7
8
9

/**
*子类滑动事件分发回调
* @param target View that initiated the nested scroll
* @param dx Horizontal scroll distance in pixels
* @param dy Vertical scroll distance in pixels
* @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
*/

public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//子类滑动事件分发回调dispatchNestedPreScroll
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
if (dx > 0) {//水平滚动距离大于0
if (target.getRight() + dx > getWidth()) {//子View右边+移动的距离越界了(父View)
dx = target.getRight() + dx - getWidth();//越界这么多
offsetLeftAndRight(dx);//父View偏移这么多
consumed[0] += dx; //将消耗掉的距离返回给子类
}
} else {//水平滚动距离<0
if (target.getLeft() + dx < 0) {
dx = target.getLeft() + dx;
offsetLeftAndRight(dx);
consumed[0] += dx;
}
}
if (dy > 0) {
if (target.getBottom() + dy > getHeight()) {
dy = target.getBottom() +dy- getHeight();
offsetTopAndBottom(dy);
consumed[1] += dy;
}
} else {
if (target.getTop() + dy < 0) {
dy = target.getTop() + dy;
offsetTopAndBottom(dy);
consumed[1] += dy;
}
}
}

函数的回调关系如下:

左边的子View回调右边的父View的方法

http://7xqumq.com1.z0.glb.clouddn.com/duwei_nestedScroll.png

参考Demo:

https://github.com/liuxiangtian/NestedScrollDemo

文章目录
  1. 1. 概述:
    1. 1.1. 前几天看到这样一个效果
    2. 1.2. 很想知道它的实现原理,于是看了一下源码,学到了一个新的东西:嵌套滑动
  2. 2. 背景
    1. 2.1. 在传统的View的事件分发机制中,事件一级一级的传递下去,直到被感兴趣的View拦截并消耗处理,如果所有的View都不拦截处理,那么最后一个View将会抛弃该事件。即:一个事件如果传给了子View那么父View将不可能再得到该事件。
  3. 3. 类:
    1. 3.1. 为了实现嵌套循环,5.0中提供了两个接口两个类:
  4. 4. 主要方法
  5. 5. 函数的回调关系如下:
    1. 5.1. 左边的子View回调右边的父View的方法
  6. 6. 参考Demo: