国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

從源碼角度分析NestedScrolling

2019-11-09 17:42:57
字體:
來源:轉載
供稿:網友

通過CoordinatorLayout可以實現許多炫酷的效果,大家可以參考我之前一篇博客:

一起玩轉CoordinatorLayout

其實CoordinatorLayout就是利用NestedScrolling(嵌套滑動機制)來完成復雜的滑動交互。NestedScrolling是Android 5.0之后為我們提供的新特性,降低了使用傳統事件分發機制處理嵌套滑動的難度,用于給子view與父view提供更好的交互。

今天就從源碼的角度一起分析NestedScrolling,關于NestedScrolling的實現,有以下幾個主要類需要關注:

NestedScrollingParent 嵌套滑動父view接口 NestedScrollingChild 嵌套滑動子view接口 NestedScrollingParentHelper 嵌套滑動父view接口的代理實現 NestedScrollingChildHelper 嵌套滑動子view接口的代理實現

我們先來看看NestedScrollingParent中的幾個實現方法:

/** * 父View是否允許嵌套滑動 * * @param child 包含嵌套滑動父類的子View * @param target 實現嵌套滑動的子View * @param nestedScrollAxes 嵌套滑動方向,水平豎直或都支持 */ @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { return super.onStartNestedScroll(child, target, nestedScrollAxes); } /** * onStartNestedScroll()方法返回true會調用該函數 * 參數與onStartNestedScroll一致 */ @Override public void onNestedScrollAccepted(View child, View target, int axes) { super.onNestedScrollAccepted(child, target, axes); } /** * 嵌套滑動結束時調用 * * @param target 實現嵌套滑動的子View */ @Override public void onStopNestedScroll(View target) { super.onStopNestedScroll(target); } /** * 嵌套滑動子View的滑動情況(進度) * * @param target 實現嵌套滑動的子View * @param dxConsumed 水平方向上嵌套滑動的子View消耗(滑動)的距離 * @param dyConsumed 豎直方向上嵌套滑動的子View消耗(滑動)的距離 * @param dxUnconsumed 水平方向上嵌套滑動的子View未消耗(未滑動)的距離 * @param dyUnconsumed 豎直方向上嵌套滑動的子View未消耗(未滑動)的距離 */ @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); } /** * 嵌套滑動子View滑動之前的準備工作 * * @param target 實現嵌套滑動的子View * @param dx 水平方向上嵌套滑動的子View滑動的總距離 * @param dy 豎直方向上嵌套滑動的子View滑動的總距離 * @param consumed consumed[0]水平方向與consumed[1]豎直方向上父View消耗(滑動)的距離 */ @Override public void onNestedPReScroll(View target, int dx, int dy, int[] consumed) { super.onNestedPreScroll(target, dx, dy, consumed); } /** * 嵌套滑動子View的fling(滑行)情況 * * @param target 實現嵌套滑動的子View * @param velocityX 水平方向上的速度 * @param velocityY 豎直方向上的速度 * @param consumed 子View是否消耗fling * @return true 父View是否消耗了fling */ @Override public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { return super.onNestedFling(target, velocityX, velocityY, consumed); } /** * 嵌套滑動子View fling(滑行)前的準備工作 * * @param target 實現嵌套滑動的子View * @param velocityX 水平方向上的速度 * @param velocityY 豎直方向上的速度 * @return true 父View是否消耗了fling */ @Override public boolean onNestedPreFling(View target, float velocityX, float velocityY) { return super.onNestedPreFling(target, velocityX, velocityY); } /** * 嵌套滑動方向 * * @return 水平豎直或都支持 */ @Override public int getNestedScrollAxes() { return super.getNestedScrollAxes(); }

接下來看看NestedScrollingChild中的實現方法:

/** * 設置是否支持嵌套滑動 * * @param enabled true與false表示支持與不支持 */ @Override public void setNestedScrollingEnabled(boolean enabled) { super.setNestedScrollingEnabled(enabled); } /** * 判斷嵌套滑動是否可用 * * @return true表示支持嵌套滑動 */ @Override public boolean isNestedScrollingEnabled() { return super.isNestedScrollingEnabled(); } /** * 開始嵌套滑動 * * @param axes 方向軸,水平方向與豎直方向 * @return */ @Override public boolean startNestedScroll(int axes) { return super.startNestedScroll(axes); } /** * 停止嵌套滑動 */ @Override public void stopNestedScroll() { super.stopNestedScroll(); } /** * 判斷父View是否支持嵌套滑動 * * @return true與false表示支持與不支持 */ @Override public boolean hasNestedScrollingParent() { return super.hasNestedScrollingParent(); } /** * 處理滑動事件 * * @param dxConsumed 水平方向上消耗(滑動)的距離 * @param dyConsumed 豎直方向上消耗(滑動)的距離 * @param dxUnconsumed 水平方向上未消耗(未滑動)的距離 * @param dyUnconsumed 豎直方向上未消耗(未滑動)的距離 * @param offsetInWindow 窗體偏移量 * @return true表示事件已經分發,false表示沒有分發 */ @Override public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { return super.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow); } /** * 處理滑動事件前的準備工作 * * @param dx 水平方向上滑動的距離 * @param dy 豎直方向上滑動的距離 * @param consumed 父view消耗的距離 * @param offsetInWindow 窗體偏移量 * @return 父View是否處理了嵌套滑動 */ @Override public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { return super.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); } /** * fling(滑行)前的準備工作 * * @param velocityX 水平方向上的速度 * @param velocityY 豎直方向上的速度 * @param consumed 是否被消耗 * @return true表示被消耗,false反之 */ @Override public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { return super.dispatchNestedFling(velocityX, velocityY, consumed); } /** * fling(滑行)時調用 * * @param velocityX 水平方向上的速度 * @param velocityY 豎直方向上的速度 * @return true表示被消耗,false反之 */ @Override public boolean dispatchNestedPreFling(float velocityX, float velocityY) { return super.dispatchNestedPreFling(velocityX, velocityY); }

實際應用中,嵌套滑動中的父view實現NestedScrollingParent接口,嵌套滑動中的子view實現NestedScrollingChild接口。NestedScrollingParentHelper和NestedScrollingChildHelper是兩個輔助類,我們只需要在對應的接口方法中調用這些輔助類的實現即可。

OK,準備工作到此結束。參考網上資料寫了一個簡單的例子,先看最終的效果圖:

這里寫圖片描述

最終實現的效果如上所示,通過這個實例來分析完整的嵌套滑動流程以及它們之間的分工合作。

1.子view是嵌套滑動的發起者,父view是嵌套滑動的處理者。首先在子view中允許設置嵌套滑動:

private void init() { nestedScrollingChildHelper = new NestedScrollingChildHelper(this); setNestedScrollingEnabled(true); }

2.調用startNestedScroll()方法開始嵌套滑動,并設置滑動方向:

case MotionEvent.ACTION_DOWN: { mDownX = x; mDownY = y; //通知父View開始嵌套滑動,并設置滑動方向(水平豎直方向都支持) startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL); break; }

這時候父view的onStartNestedScroll方法將會被回調,返回true表示允許此次嵌套滑動:

@Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { return true; }

3.view開始滑動之前,會調用dispatchNestedPreScroll方法確定父view是否需要滑動。如果父view需要滑動,會消耗的距離放在consumed中,返回給子view,子view根據父view消耗的距離重新計算自己需要滑動的距離,進行滑動;如果父view不需要滑動,則子View自身處理滑動事件:

case MotionEvent.ACTION_MOVE: { int dx = x - mDownX; int dy = y - mDownY; //如果父View處理滑動事件 if (dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)) { //減去父View消耗的距離 dx -= consumed[0]; dy -= consumed[1]; } offsetLeftAndRight(dx); offsetTopAndBottom(dy); break; }

這時候父view的onNestedPreScroll方法將會被回調,協同處理滑動事件:

@Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { super.onNestedPreScroll(target, dx, dy, consumed); //向右滑動 if (dx > 0) { //滑動到邊界 if (target.getRight() + dx > getWidth()) { dx = target.getRight() + dx - getWidth(); //父View消耗 offsetLeftAndRight(dx); consumed[0] += dx; } } //向左滑動 else { if (target.getLeft() + dx < 0) { dx = dx + target.getLeft(); //父View消耗 offsetLeftAndRight(dx); consumed[0] += dx; } } //向下滑動 if (dy > 0) { if (target.getBottom() + dy > getHeight()) { dy = target.getBottom() + dy - getHeight(); //父View消耗 offsetTopAndBottom(dy); consumed[1] += dy; } } //向上滑動 else { if (target.getTop() + dy < 0) { dy = dy + target.getTop(); //父View消耗 offsetTopAndBottom(dy); consumed[1] += dy; } } }

4.子view計算完自己的滑動距離進行滑動之后,調用dispatchNestedScroll方法進行滑動:

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

5.如果需要停止嵌套滑動,子view調用stopNestedScroll方法,父view的onStopNestedScroll方法被回調結束滑動:

case MotionEvent.ACTION_UP: { //結束嵌套滑動 stopNestedScroll(); break; }

至此,我們已經經歷了一次完整的嵌套滑動流程,實際上內部都是通過NestedScrollingChildHelper實現的,我們只需要在恰當的地方傳入參數調用方法即可。

關于NestedScrollingParentHelper源碼解析可以參考下面的博客:

NestedScrollingParent,NestedScrollingParentHelper 詳解

希望能對你有所幫助,源碼已經同步上傳到github上:

https://github.com/18722527635/AndroidArtStudy

歡迎star,fork,提issues,一起進步,下一篇再見~


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 房产| 江陵县| 渑池县| 古浪县| 元阳县| 五台县| 徐汇区| 梧州市| 广灵县| 土默特右旗| 加查县| 望江县| 景德镇市| 武隆县| 镇安县| 漯河市| 屯门区| 灵山县| 彭水| 天峻县| 铜鼓县| 盘锦市| 富民县| 闸北区| 崇明县| 公安县| 赫章县| 松桃| 高邑县| 双城市| 温州市| 凤阳县| 安平县| 西宁市| 共和县| 怀远县| 通州区| 南召县| 诸城市| 常山县| 慈溪市|