转载请注明出处 http://blog.csdn.net/u011453163/article/details/52751110
国庆假期终于要结束了,懒散了几天 。加上之前的微信小程序事件,赶紧写篇博客压压惊。
android 传统的事件派发 从外到内层层派发,当其中某一组件消化了事件之后即事件派发结束,无法回馈到外部组件。
Android5.0以后 官方提供了一套嵌套滑动的事件派发机制。当然也兼容5.0一下版本
support-v4 版本应该在22以上
提供了几个类
public interface NestedScrollingChild
public class NestedScrollingChildHelper (至关重要)
public interface NestedScrollingParent
public class NestedScrollingParentHelper
总共是两个接口和两个辅助类
用一个简单的图表示一下我的理解
NestedScrollingChild中的方法
这个接口的方法在5.0以上的View已经包含了 在5.0以下版本是没用这些方法的,所以NestedScrollingChild基本上就为了兼容低版本所提供的接口类。同理
NestedScrollingParent也是为低版本提供一些回调方法,但是它还是一个非常重要的角色
接下来是两个辅助类
NestedScrollingParentHelper基本没做什么事
NestedScrollingChildHelper则是一个非常重要的存在,基本是嵌套滑动的所有逻辑都在从这个类里面实现的。也是今天的关键。
这是四个类的方法。
根据上面简单的图解 嵌套滑动的关键是事件从内往外传递。这也是源码的思想。
嵌套滑动最开始是从
NestedScrollingChildHelper 的 startNestedScroll开始的,我也从这里开始分析源码的走向
public boolean startNestedScroll(int axes) {
if (hasNestedScrollingParent()) {
// Already in progress
return true;
}
if (isNestedScrollingEnabled()) {
ViewParent p = mView.getParent();
View child = mView;
while (p != null) {
if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
mNestedScrollingParent = p;
ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
return true;
}
if (p instanceof View) {
child = (View) p;
}
p = p.getParent();
}
}
return false;
}
这段代码的大致意思是要告诉外层组件 要开始滑动啦。外层组件??谁是外层的组件,外层组件那么怎么知道具体要通知谁呢??
可以看到这段代码有一个遍历外部组件的过程
if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
mNestedScrollingParent = p;
ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
return true;
}
谁是满足条件的呢???
跟进源码
public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
int nestedScrollAxes) {
return IMPL.onStartNestedScroll(parent, child, target, nestedScrollAxes);
}
源码真心不错。
@Override
public void onNestedScrollAccepted(ViewParent parent, View child, View target,
int nestedScrollAxes) {
if (parent instanceof NestedScrollingParent) {
((NestedScrollingParent) parent).onNestedScrollAccepted(child, target,
nestedScrollAxes);
}
}
看到里面的实现 可以看出这外层的组件必须是实现了NestedScrollingParent接口的 这也是刚刚为什么说是一个重要的角色的原因。
外层的组件找到了 那剩下的就是的就是通知啦
其实从startNestedScroll这个方法的思想 其他的滑动事件的回馈基本都是一样的了
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
if (isNestedScrollingEnabled() && mNestedScrollingParent != null) {
if (dx != 0 || dy != 0) {
int startX = 0;
int startY = 0;
if (offsetInWindow != null) {
mView.getLocationInWindow(offsetInWindow);
startX = offsetInWindow[0];
startY = offsetInWindow[1];
}
if (consumed == null) {
if (mTempNestedScrollConsumed == null) {
mTempNestedScrollConsumed = new int[2];
}
consumed = mTempNestedScrollConsumed;
}
consumed[0] = 0;
consumed[1] = 0;
ViewParentCompat.onNestedPreScroll(mNestedScrollingParent, mView, dx, dy, consumed);
if (offsetInWindow != null) {
mView.getLocationInWindow(offsetInWindow);
offsetInWindow[0] -= startX;
offsetInWindow[1] -= startY;
}
return consumed[0] != 0 || consumed[1] != 0;
} else if (offsetInWindow != null) {
offsetInWindow[0] = 0;
offsetInWindow[1] = 0;
}
}
return false;
}
这里一大堆关于坐标 位置的计算 都没啥用 关键还得传递出去
ViewParentCompat.onNestedPreScroll(mNestedScrollingParent, mView, dx, dy, consumed);
这才是关键
跟进源码 看到的原理还是一样的
@Override
public void onNestedPreScroll(ViewParent parent, View target, int dx, int dy,
int[] consumed) {
if (parent instanceof NestedScrollingParent) {
((NestedScrollingParent) parent).onNestedPreScroll(target, dx, dy, consumed);
}
}
一个简单的接口回调。。。 基本上的源码流程就是这样啦
用法是
1 外层组件 实现NestedScrollingParent
内层组件 实现NestedScrollingChild
2 内层组件在时间监听的时候 使用NestedScrollingChildHelper做一些事件的向外通知
3 外层组件通过回调的方法处理实际业务
关于嵌套滑动的源码解析到此为止 有什么不对欢迎指出。