WebView 实现 NestedScrollingChild

承接上文 NestedScrollingParent 实现复杂交互效果 ,在 NestedScroll 这一套流程中,NestedScrollingChild 不用考虑怎么去通知父布局如何滚动,只需要负责自己的事情:产生滚动事件,并将事件共享出去产生 fling 事件,共享 fling

NestedScrollingParent 简称 NP
NestedScrollingChild 简称 NC

  1. NC 调用 startNestedScroll() 通知 NP 的 onStartNestedScroll (具体是怎么通知到 NP 的参考 源码)

  2. NC 产生一个 Touch 事件,如滚动 5 个像素,然后调用 dispatchNestedPreScroll 通知 NP 自己要滚动 5个像素, NP 收到通知后根据自身需要进行消费,如消费了 2 个像素,然后将结果通知到 NC

  3. NC 将剩下的 3 个像素进行自身消费,如此时滚动了 2 个像素到达了边界,还剩下 1 个像素没有消费掉,然后就调用 dispatchNestedScroll 将剩下的未消费的 1 个像素共享出去。

  4. Fling 事件类似 Scroll 事件

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
* Created by hanks on 16/8/30.
*/
public class CodeWebView extends WebView implements NestedScrollingChild, ScrollListener {

public static final String TAG = CodeWebView.class.getSimpleName();
public static final int UP = 1;
public static final int DOWN = -1;
private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = new int[2];
private final int mTouchSlop;
private final int mMinimumVelocity;
private final int mMaximumVelocity;
private int direction = DOWN; // TODO 还需要同步到父布局的方向
private int mLastMotionY;
private int mNestedYOffset;
private NestedScrollingChildHelper mChildHelper;
private VelocityTracker mVelocityTracker;
private boolean allowFly;
private int downY;
private float mDownY = -1;

public CodeWebView(Context context) {
this(context, null);
}

public CodeWebView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public CodeWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

mChildHelper = new NestedScrollingChildHelper(this); // 辅助类
setNestedScrollingEnabled(true); // 设置支持 NestedScrolling

final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();

}

@Override
public boolean onTouchEvent(MotionEvent event) {

boolean eventAddedToVelocityTracker = false;

final int action = MotionEventCompat.getActionMasked(event);
final int actionIndex = MotionEventCompat.getActionIndex(event);

if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}

switch (action) {
case MotionEvent.ACTION_DOWN:
allowFly = false;
downY = (int) event.getRawY();
// 开始 NestedScroll
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
break;
case MotionEvent.ACTION_MOVE:
int moveY = (int) event.getRawY();
int dy = -(moveY - downY); //滚动方法的方向跟坐标是相反的,所以这里要加一个负号
downY = moveY;
//在consumed 中就是父类滑动的距离,
if (dispatchNestedPreScroll(0, dy, mScrollConsumed, mScrollOffset)) {
dy -= mScrollConsumed[1]; // 减去父类消费的距离
scrollBy(0, dy); // 剩下的自己消费
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:

mVelocityTracker.addMovement(event);
eventAddedToVelocityTracker = true;
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int mScrollPointerId = MotionEventCompat.getPointerId(event, actionIndex);
float vY = -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId);
// 产生 fling 事件
if (Math.abs(vY) > mMinimumVelocity && !dispatchNestedPreFling(0, vY)) {
dispatchNestedFling(0, vY, true);
logi("dispatchNestedFling");
}
resetTouch();
break;
}
if (!eventAddedToVelocityTracker) {
mVelocityTracker.addMovement(event);
}
return true;

}

private void resetTouch() {
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
stopNestedScroll();
}


@Override
public void setNestedScrollingEnabled(boolean enabled) {
mChildHelper.setNestedScrollingEnabled(enabled);
}

@Override
public boolean startNestedScroll(int axes) {
return mChildHelper.startNestedScroll(axes);
}

@Override
public void stopNestedScroll() {
mChildHelper.stopNestedScroll();
}

@Override
public boolean hasNestedScrollingParent() {
return mChildHelper.hasNestedScrollingParent();
}

@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
}

@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}

@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}

@Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}

public void log(String s) {
Log.e(TAG, s);
}

public void logi(String s) {
Log.i(TAG, s);
}

public void logw(String s) {
Log.w(TAG, s);
}
}

文章来自: https://hanks.pub