編輯:關於Android編程
現在講到android的機制,就是事件分發,事件攔截。但我不知道大家聽沒聽說過嵌套的滑動機制,准確的可以理解成把事件分發,事件攔截綜合在一起。
如果聽說過這個的,你們第一個應該是想到的CoordinatorLayout。也就是只要自己定義個layout實現NestedScrollingChild。就可以實現這些機制,但我今天講的重點並不是Child。而是Parent。眾所周知,需要用到滾動機制的無非就2種。第一個,scrollview。第二個列表。而現在的listview早已被recyclerview取代了。而recyclerview已經實現了nestedscrollingchild的接口。關於scrollview。v4包裡有一個NestedScrollView也實現滑動機制的接口。
今天我們通過觀察CoordinatorLayout的源碼來了解nestedscrollingparent。
首先我們來看實現接口需要實現的幾個方法:
public interface NestedScrollingParent { /** * 有嵌套滑動到來了,問下該父View是否接受嵌套滑動 * @param child 嵌套滑動對應的父類的子類(因為嵌套滑動對於的父View不一定是一級就能找到的,可能挑了兩級父View的父View,child的輩分>=target) * @param target 具體嵌套滑動的那個子類 * @param nestedScrollAxes 支持嵌套滾動軸。水平方向,垂直方向,或者不指定 * @return 是否接受該嵌套滑動 */ public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes); /** * 該父View接受了嵌套滑動的請求該函數調用。onStartNestedScroll返回true該函數會被調用。 * 參數和onStartNestedScroll一樣 */ public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes); /** * 停止嵌套滑動 * @param target 具體嵌套滑動的那個子類 */ public void onStopNestedScroll(View target); /** * 嵌套滑動的子View在滑動之後報告過來的滑動情況 * * @param target 具體嵌套滑動的那個子類 * @param dxConsumed 水平方向嵌套滑動的子View滑動的距離(消耗的距離) * @param dyConsumed 垂直方向嵌套滑動的子View滑動的距離(消耗的距離) * @param dxUnconsumed 水平方向嵌套滑動的子View未滑動的距離(未消耗的距離) * @param dyUnconsumed 垂直方向嵌套滑動的子View未滑動的距離(未消耗的距離) */ public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed); /** * 在嵌套滑動的子View未滑動之前告訴過來的准備滑動的情況 * @param target 具體嵌套滑動的那個子類 * @param dx 水平方向嵌套滑動的子View想要變化的距離 * @param dy 垂直方向嵌套滑動的子View想要變化的距離 * @param consumed 這個參數要我們在實現這個函數的時候指定,回頭告訴子View當前父View消耗的距離 * consumed[0] 水平消耗的距離,consumed[1] 垂直消耗的距離 好讓子view做出相應的調整 */ public void onNestedPreScroll(View target, int dx, int dy, int[] consumed); /** * 嵌套滑動的子View在fling之後報告過來的fling情況 * @param target 具體嵌套滑動的那個子類 * @param velocityX 水平方向速度 * @param velocityY 垂直方向速度 * @param consumed 子view是否fling了 * @return true 父View是否消耗了fling */ public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed); /** * 在嵌套滑動的子View未fling之前告訴過來的准備fling的情況 * @param target 具體嵌套滑動的那個子類 * @param velocityX 水平方向速度 * @param velocityY 垂直方向速度 * @return true 父View是否消耗了fling */ public boolean onNestedPreFling(View target, float velocityX, float velocityY); /** * 獲取嵌套滑動的軸 * @see ViewCompat#SCROLL_AXIS_HORIZONTAL 垂直 * @see ViewCompat#SCROLL_AXIS_VERTICAL 水平 * @see ViewCompat#SCROLL_AXIS_NONE 都支持 */ public int getNestedScrollAxes(); }
執行過程:
在實現的NestedScrollingParent幾個接口中(onNestedScrollAccepted, onStopNestedScroll, getNestedScrollAxes)調用NestedScrollingParentHelper對應的函數。 視情況而定onNestedScroll onNestedPreScroll onNestedFling onNestedPreFling 做相應的處理。public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { boolean handled = false; final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target, nestedScrollAxes); handled |= accepted; lp.acceptNestedScroll(accepted); } else { lp.acceptNestedScroll(false); } } return handled; }onStartNestedScroll,判斷父view是否參與滾動事件,源碼是從Behavior以及使用了遞歸調用講handled=true;因為我對Behavior不是很了解,你們有興趣的可以自行研究。我們可以在這裡這裡return ture;告訴子view我會參與你的滾動事件。
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) { mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes); mNestedScrollingDirectChild = child; mNestedScrollingTarget = target; final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); if (!lp.isNestedScrollAccepted()) { continue; } final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { viewBehavior.onNestedScrollAccepted(this, view, child, target, nestedScrollAxes); } } }
2.這個是處理和子view一樣的滾動事件,如果我們自定義的話,調用
helper.onNestedScrollAccepted(child, target, axes)。他的參數和start一樣,即只要參與了滾動事件,我們就需要處理滾動事件。
public void onStopNestedScroll(View target) { mNestedScrollingParentHelper.onStopNestedScroll(target); final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); if (!lp.isNestedScrollAccepted()) { continue; } final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { viewBehavior.onStopNestedScroll(this, view, target); } lp.resetNestedScroll(); lp.resetChangedAfterNestedScroll(); } mNestedScrollingDirectChild = null; mNestedScrollingTarget = null; }
3.讓view停止滾動,此時會通知子view停止滾動事件,相當於action_up的效果。
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { final int childCount = getChildCount(); boolean accepted = false; for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); if (!lp.isNestedScrollAccepted()) { continue; } final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { viewBehavior.onNestedScroll(this, view, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); accepted = true; } } if (accepted) { dispatchOnDependentViewChanged(true); } }
4.這段的意思是:
子view滑動結束調用
dyUnconsumed < 0 向下滾
dyUnconsumed > 0 向上滾
after childview move over, dyUnconsumed <0 pull down else up
我來畫個圖方便大家理解把:
此時的view2是存在的,但是因為view1鋪滿整個屏幕,所以view2是看不見的,如果我們想讓view2滑出來。只要當view1滾動結束,使用此方法讓view2滾動出來。
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { int xConsumed = 0; int yConsumed = 0; boolean accepted = false; final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); if (!lp.isNestedScrollAccepted()) { continue; } final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { mTempIntPair[0] = mTempIntPair[1] = 0; viewBehavior.onNestedPreScroll(this, view, target, dx, dy, mTempIntPair); xConsumed = dx > 0 ? Math.max(xConsumed, mTempIntPair[0]) : Math.min(xConsumed, mTempIntPair[0]); yConsumed = dy > 0 ? Math.max(yConsumed, mTempIntPair[1]) : Math.min(yConsumed, mTempIntPair[1]); accepted = true; } } consumed[0] = xConsumed; consumed[1] = yConsumed; if (accepted) { dispatchOnDependentViewChanged(true); } }
4.這個相當於在子view滾動之前讓父view滾動。但我不理解的試,consumed[0],和consumed[1]。是告訴子view父view滾動的x,y的偏移量。為什麼源碼裡的cousumed[0]和cousumed[1]都等於0?這是當時看源碼的時候我最不理解的地方。這裡我們只要告訴子view我們滾動的x,y值。讓子view一起滾動就行了(注意:雖然是讓子view滾動,但我們效果實際是滾動父view。總不能父view滾下去,然後回來了。子view下去了就回不來了把。)如果都為0,我畫個圖或許你們就懂了。
我們想要的效果是左邊的,而如果你都給他返回0的話就是右邊的效果。這並不是我們想要的。
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { boolean handled = false; if (handled) { final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); if (!lp.isNestedScrollAccepted()) { continue; } final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { handled |= viewBehavior.onNestedFling(this, view, target, velocityX, velocityY, consumed); } } dispatchOnDependentViewChanged(true); } return handled; }
這個我更沒搞懂。他確定中間的代碼會執行?不鳥他,我們直接return false就可以了。
public boolean onNestedPreFling(View target, float velocityX, float velocityY) { boolean handled = false; final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View view = getChildAt(i); final LayoutParams lp = (LayoutParams) view.getLayoutParams(); if (!lp.isNestedScrollAccepted()) { continue; } final Behavior viewBehavior = lp.getBehavior(); if (viewBehavior != null) { handled |= viewBehavior.onNestedPreFling(this, view, target, velocityX, velocityY); } } return handled; }
這個確定沒搞錯?不應該和上面那個一樣??,依舊是return false。
public int getNestedScrollAxes() { return mNestedScrollingParentHelper.getNestedScrollAxes(); }
這個就簡單了。
我們通過看源碼了解了CoordinatorLayout的執行流程。其實如果自定義layout實現這個接口反而比CoordinatorLayout簡單很多。因為從源碼角度和我之前的注釋來講,我們的核心代碼是寫在:onNestedPreScroll()和onNestedScroll(),因為這2個一個是在子view滾動之前,一個是在子view滾動之後。
現在我們可以通過實現這個接口來自定義屬於自己的Coordinatorlayout了。我自己寫了一個才200+行代碼,而原生的快3000行,而且還不一定符合自己的效果。如果你了解了滑動機制,就去實現屬於自己的Coordinatorlayout吧。
我的android交流群:232748032
轉載請注明出處 http://blog.csdn.net/typename/ powered by miechal zhao : miechalzhao@gmail.
在 Android design support 包中提供了一種在輸入不合適字符時一直顯示的提示方式來顯示,現在已經開始在更多的應用上被使用了;這些 Android ap
一、意圖Intent我們都知道Android 應用框架中的4大基本組件分別是:活動----繪制一個屏幕的用戶界面,響應用戶交互服務----在後台運行,執行耗時操作或遠程過
本人比較懶,不說廢話,直接貼代碼,代碼後附有完整項目package test.com.opengles5_3;import android.opengl.GLES20;i