- 孩子:
- NestedScrollingChild3
- NestedScrollingChild2
- NestedScrollingChild
- 父亲
- NestedScrollingParent3
- NestedScrollingParent2
- NestedScrollingParent
嵌套滑动有两个角色:父亲和孩子
其中孩子是主动控制的一方。
android中的滑动,大体可以分为两部分:
- 第一个部分:手指和屏幕接触的TOUCH
- 第二个部分:手指离开屏幕后FLING
为了方便嵌套滑动的实现,Google提供了两个帮助类:NestedScrollingChildHelper和NestedScrollingParentHelper,简化后续开发。
嵌套滑动的主要逻辑都在两个帮助类中实现了。
流程:
setNestedScrollingEnabled,Child使能
ScrollingChild,DOWN事件
a. startNestedScroll, 会在此方法中一层层往上找有没有支持嵌套滑动的容器,即实现了 ScrollingParent 相关接口的容器
b. 找到父亲之后
- 调用父亲的onStartNestedScroll方法
- 设置父亲的类型
- 调用父亲的onNestedScrollAccepted方法
- 如果父亲同时实现了ScrollingChild,那么父亲在onNestedScrollAccepted中需要调用自身的startNestedScroll方法-
ScrollingChild,MOVE事件
a. 孩子 dispatchNestedPreScroll
b. 父亲 onNestedPreScrollc. 孩子 dispatchNestedScroll
d. 父亲 onNestedScroll -
ScrollingChild, UP事件
a. 孩子 dispatchNestedPreFling
b. 父亲 onNestedPreFlingc. 孩子 dispatchNestedFling
d. 父亲 onNestedFling
在主要流程上,都是Child发起,让Parent先消费,消费完之后,child消费。
Parent的onXXX方法都会有一个参数consumed[],用来表示Parent对这个事件消费了多少。consumed[0]代表X轴的消费量,consumed[1]代表Y周消费量。
@param consumed Output. The horizontal and vertical scroll distance consumed by this parent
为什么MOVE事件和UP事件都是两段式的?
分为pre阶段和第二阶段。
因为我们的Child可能在Parent中间。
当Child滑动完之后,Parent可能还能动。
以上都Child带着Parent动。
如果需要Parent带着Child动,这个没在官方的嵌套滑动中支持,需要自己处理。
Parent带着Child动的应用场景是当Parent处于Fling时,速度比较快,必须要这个Fling的速度继续给Child,不然整个滑动会显得很生硬。
Parent带着Child动,流程
- Parent可以拿到速度,fling方法拿到速度(velocity)。
- 根据速度可以计算出应该滑动的距离
- 用应该滑动的距离 - 实际滑动的距离(这个需要Parent自己记录) = Child应该滑动的距离
- Child应该滑动的距离 转化为 Child应该滑动时的速度
- 通过Child的fling方法滑动Child
处理Fling的工具类:
public class FlingHelper {
private static float DECELERATION_RATE = ((float) (Math.log(0.78d) / Math.log(0.9d)));
private static float mFlingFriction = ViewConfiguration.getScrollFriction();
private static float mPhysicalCoeff;
public FlingHelper(Context context) {
mPhysicalCoeff = context.getResources().getDisplayMetrics().density * 160.0f * 386.0878f * 0.84f;
}
private double getSplineDeceleration(int i) {
return Math.log((double) ((0.35f * ((float) Math.abs(i))) / (mFlingFriction * mPhysicalCoeff)));
}
private double getSplineDecelerationByDistance(double d) {
return ((((double) DECELERATION_RATE) - 1.0d) * Math.log(d / ((double) (mFlingFriction * mPhysicalCoeff)))) / ((double) DECELERATION_RATE);
}
public double getSplineFlingDistance(int i) {
return Math.exp(getSplineDeceleration(i) * (((double) DECELERATION_RATE) / (((double) DECELERATION_RATE) - 1.0d))) * ((double) (mFlingFriction * mPhysicalCoeff));
}
public int getVelocityByDistance(double d) {
return Math.abs((int) (((Math.exp(getSplineDecelerationByDistance(d)) * ((double) mFlingFriction)) * ((double) mPhysicalCoeff)) / 0.3499999940395355d));
}
}