- CompoundButton 源码分析
- LinearLayout 源码分析
- SearchView 源码解析
- LruCache 源码解析
- ViewDragHelper 源码解析
- BottomSheets 源码解析
- Media Player 源码分析
- NavigationView 源码解析
- Service 源码解析
- Binder 源码分析
- Android 应用 Preference 相关及源码浅析 SharePreferences 篇
- ScrollView 源码解析
- Handler 源码解析
- NestedScrollView 源码解析
- SQLiteOpenHelper/SQLiteDatabase/Cursor 源码解析
- Bundle 源码解析
- LocalBroadcastManager 源码解析
- Toast 源码解析
- TextInputLayout
- LayoutInflater 和 LayoutInflaterCompat 源码解析
- TextView 源码解析
- NestedScrolling 事件机制源码解析
- ViewGroup 源码解析
- StaticLayout 源码分析
- AtomicFile 源码解析
- AtomicFile 源码解析
- Spannable 源码分析
- Notification 之 Android 5.0 实现原理
- CoordinatorLayout 源码分析
- Scroller 源码解析
- SwipeRefreshLayout 源码分析
- FloatingActionButton 源码解析
- AsyncTask 源码分析
- TabLayout 源码解析
NestedScrolling 事件机制源码解析
Android 在发布 5.0(Lollipop)版本之后,Google 为我们提供了嵌套滑动(NestedScrolling) 的特性,今天就由我带大家去看看嵌套滑动机制是怎样的原理?
首先,请随意瞄一瞄以下 4 个类:
有个大概印象就好,如果你一看就懂,那就不要浪费时间继续看下去了,啊哈哈哈!
从 NestedScrollingChild 说起,它是个接口,定义如下:
public interface NestedScrollingChild {
/**
* 设置嵌套滑动是否能用
*
* @param enabled true to enable nested scrolling, false to disable
*/
public void setNestedScrollingEnabled(boolean enabled);
/**
* 判断嵌套滑动是否可用
*
* @return true if nested scrolling is enabled
*/
public boolean isNestedScrollingEnabled();
/**
* 开始嵌套滑动
*
* @param axes 表示方向轴,有横向和竖向
*/
public boolean startNestedScroll(int axes);
/**
* 停止嵌套滑动
*/
public void stopNestedScroll();
/**
* 判断是否有父 View 支持嵌套滑动
* @return whether this view has a nested scrolling parent
*/
public boolean hasNestedScrollingParent();
/**
* 在子 View 的 onInterceptTouchEvent 或者 onTouch 中,调用该方法通知父 View 滑动的距离
*
* @param dx x 轴上滑动的距离
* @param dy y 轴上滑动的距离
* @param consumed 父 view 消费掉的 scroll 长度
* @param offsetInWindow 子 View 的窗体偏移量
* @return 支持的嵌套的父 View 是否处理了 滑动事件
*/
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
/**
* 子 view 处理 scroll 后调用
*
* @param dxConsumed x 轴上被消费的距离(横向)
* @param dyConsumed y 轴上被消费的距离(竖向)
* @param dxUnconsumed x 轴上未被消费的距离
* @param dyUnconsumed y 轴上未被消费的距离
* @param offsetInWindow 子 View 的窗体偏移量
* @return true if the event was dispatched, false if it could not be dispatched.
*/
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
/**
* 滑行时调用
*
* @param velocityX x 轴上的滑动速率
* @param velocityY y 轴上的滑动速率
* @param consumed 是否被消费
* @return true if the nested scrolling parent consumed or otherwise reacted to the fling
*/
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
/**
* 进行滑行前调用
*
* @param velocityX x 轴上的滑动速率
* @param velocityY y 轴上的滑动速率
* @return true if a nested scrolling parent consumed the fling
*/
public boolean dispatchNestedPreFling(float velocityX, float velocityY);
}
以上方法数不多,请详细看看。
那它的作用是什么呢?让我们想想一种场景:CoordinatorLayout 里嵌套着 RecyclerView 和 Toolbar,我们上下滑动 RecyclerView 的时候,Toolbar 会随之显现隐藏,这是典型的嵌套滑动机制情景。这里,RecyclerView 作为嵌套的子 View,我们猜测,它一定实现了 NestedScrollingChild 接口(去看看它的定义就知道了,猜你个头)
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
..................................................................................
}
所以 RecyclerView 实现了 NestedScrollingChild 接口里的方法,我们在跟进去看看各个方法是怎么实现的?
@Override
public void setNestedScrollingEnabled(boolean enabled) {
getScrollingChildHelper().setNestedScrollingEnabled(enabled);
}
@Override
public boolean isNestedScrollingEnabled() {
return getScrollingChildHelper().isNestedScrollingEnabled();
}
@Override
public boolean startNestedScroll(int axes) {
return getScrollingChildHelper().startNestedScroll(axes);
}
@Override
public void stopNestedScroll() {
getScrollingChildHelper().stopNestedScroll();
}
@Override
public boolean hasNestedScrollingParent() {
return getScrollingChildHelper().hasNestedScrollingParent();
}
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow) {
return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, offsetInWindow);
}
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
}
@Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
}
从上面的代码可以看出,全部都交给 getScrollingChildHelper()
这个方法的返回对象处理了,看看这个方法是怎么实现的。
private NestedScrollingChildHelper getScrollingChildHelper() {
if (mScrollingChildHelper == null) {
mScrollingChildHelper = new NestedScrollingChildHelper(this);
}
return mScrollingChildHelper;
}
对,NestedScrollingChild 接口的方法都交给 NestedScrollingChildHelper 这个代理对象处理了。现在我们继续深入,随意挑个,分析下 NestedScrollingChildHelper 中开始嵌套滑动 startNestedScroll(int axes)
方法是怎么实现的。
NestedScrollingChildHelper#startNestedScroll
public boolean startNestedScroll(int axes) {
if (hasNestedScrollingParent()) {
return true;
}
if (isNestedScrollingEnabled()) {//判断是否可以滑动
ViewParent p = mView.getParent();
View child = mView;
while (p != null) {
if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {//回调了父 View 的 onStartNestedScroll 方法
mNestedScrollingParent = p;
ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
return true;
}
if (p instanceof View) {
child = (View) p;
}
p = p.getParent();
}
}
return false;
}
以上方法主要做了:
- 判断是否有嵌套滑动的父 View,返回值 true 表示找到了嵌套滑动的父 View 和同意一起处理 Scroll 事件。
- 用 While 的方式寻找最近嵌套滑动的父 View ,如果找到调用父 view 的
onNestedScrollAccepted
.
从这里至少可以得出 子 view 在调用某个方法都会回调嵌套父 view 相应的方法,比如子 view 开始了 startNestedScroll
,如果嵌套父 view 存在,就会回调父 view 的 onStartNestedScroll
、 onNestedScrollAccepted
方法。
有兴趣的朋友在去看看
NestedScrollingChildHelper#dispatchNestedPreScroll
NestedScrollingChildHelper#dispatchNestedScroll
NestedScrollingChildHelper#stopNestedScroll
的实现。
接下来,在来看看嵌套滑动父 view NestedScrollingParent,定义如下
public interface NestedScrollingParent {
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
public void onStopNestedScroll(View target);
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed);
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
public boolean onNestedFling(View target, float velocityX,
float velocityY,boolean consumed);
public boolean onNestedPreFling(View target, float velocityX, float velocityY);
public int getNestedScrollAxes();
}
你会发现,其实和子 view 差不多的方法,大致一一对应关系,而且它的具体实现也交给了 NestedScrollingParentHelper 这个代理类,这和我们上文的方式是一样的,就不再重复了。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论