編輯:關於Android編程
Behavior對象是怎麼被實例化的
CoordinatorLayout裡面的子View是怎麼拿到Behavior對象的。設置Bevior有兩種方式
1. 代碼裡面直接設置(LayoutParams)child.getLayoutParams().setBehavior()這個就好說了。
2. XML裡面去設置app:layout_behavior=”” 。
對應第二種情況Behavior是怎麼實例化的。
CoordinatorLayout裡面LayoutParams兩個參數的構造函數。
LayoutParams(Context context, AttributeSet attrs) { super(context, attrs); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CoordinatorLayout_LayoutParams); this.gravity = a.getInteger( R.styleable.CoordinatorLayout_LayoutParams_android_layout_gravity, Gravity.NO_GRAVITY); mAnchorId = a.getResourceId(R.styleable.CoordinatorLayout_LayoutParams_layout_anchor, View.NO_ID); this.anchorGravity = a.getInteger( R.styleable.CoordinatorLayout_LayoutParams_layout_anchorGravity, Gravity.NO_GRAVITY); this.keyline = a.getInteger(R.styleable.CoordinatorLayout_LayoutParams_layout_keyline, -1); mBehaviorResolved = a.hasValue( R.styleable.CoordinatorLayout_LayoutParams_layout_behavior); if (mBehaviorResolved) { mBehavior = parseBehavior(context, attrs, a.getString( R.styleable.CoordinatorLayout_LayoutParams_layout_behavior)); } a.recycle(); }
第20行是否設置了app:layout_behavior=”” 如果設置了調用parseBehavior函數。利用反射去實例化出Behavior對象。
Behavior簡單分析
Behavior都是配合CoordinatorLayout來使用的。
對於Behavior我們分三種情況來考慮。
1. Behavior的onInterceptTouchEvent + onTouchEvent。
2. Behavior的layoutDependsOn + onDependentViewChanged + onDependentViewRemoved。View引起的變化。
3. Behavior的onStartNestedScroll + onNestedScrollAccepted + onStopNestedScroll + onNestedScroll + onNestedPreScroll + onNestedFling + onNestedPreFling。嵌套滑動引起的變化。
大概的去知道每種情況裡面每個函數的調用時機。和大概的作用。
Behavior的onInterceptTouchEvent + onTouchEvent
分兩部分來考慮。
1. CoordinatorLayout裡面是怎麼調用到我們指定View的Behavior的onInterceptTouchEvent和onTouchEvent裡面去的。
2. View的Behavior裡面onInterceptTouchEvent和onTouchEvent裡面干了什麼事情。(這個一般是我們自定義Behavior的時候處理,這個先不管)。
CoordinatorLayout裡面是怎麼調用到我們指定View的Behavior的onInterceptTouchEvent和onTouchEvent裡面去的。直接CoordinatorLayout的onInterceptTouchEvent函數和onTouchEvent函數裡面了先CoordinatorLayout類的onInterceptTouchEvent函數。
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { MotionEvent cancelEvent = null; final int action = MotionEventCompat.getActionMasked(ev); // Make sure we reset in case we had missed a previous important event. if (action == MotionEvent.ACTION_DOWN) { resetTouchBehaviors(); } final boolean intercepted = performIntercept(ev, TYPE_ON_INTERCEPT); if (cancelEvent != null) { cancelEvent.recycle(); } if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { resetTouchBehaviors(); } return intercepted; }
第12行調用了performIntercept函數,參數type TYPE_ON_INTERCEPT表示從onInterceptTouchEvent函數進來的,TYPE_ON_TOUCH表示從onTouchEvent函數進來的。
private boolean performIntercept(MotionEvent ev, final int type) { boolean intercepted = false; boolean newBlock = false; MotionEvent cancelEvent = null; final int action = MotionEventCompat.getActionMasked(ev); final List<View> topmostChildList = mTempList1; getTopSortedChildren(topmostChildList); // Let topmost child views inspect first final int childCount = topmostChildList.size(); for (int i = 0; i < childCount; i++) { final View child = topmostChildList.get(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final Behavior b = lp.getBehavior(); if ((intercepted || newBlock) && action != MotionEvent.ACTION_DOWN) { // Cancel all behaviors beneath the one that intercepted. // If the event is "down" then we don't have anything to cancel yet. if (b != null) { if (cancelEvent != null) { final long now = SystemClock.uptimeMillis(); cancelEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); } switch (type) { case TYPE_ON_INTERCEPT: b.onInterceptTouchEvent(this, child, cancelEvent); break; case TYPE_ON_TOUCH: b.onTouchEvent(this, child, cancelEvent); break; } } continue; } if (!intercepted && b != null) { switch (type) { case TYPE_ON_INTERCEPT: intercepted = b.onInterceptTouchEvent(this, child, ev); break; case TYPE_ON_TOUCH: intercepted = b.onTouchEvent(this, child, ev); break; } if (intercepted) { mBehaviorTouchView = child; } } // Don't keep going if we're not allowing interaction below this. // Setting newBlock will make sure we cancel the rest of the behaviors. final boolean wasBlocking = lp.didBlockInteraction(); final boolean isBlocking = lp.isBlockingInteractionBelow(this, child); newBlock = isBlocking && !wasBlocking; if (isBlocking && !newBlock) { // Stop here since we don't have anything more to cancel - we already did // when the behavior first started blocking things below this point. break; } } topmostChildList.clear(); return intercepted; }
第14行開始,遍歷CoordinatorLayout所有的View,第19行第一個if做的事情是如果現在已經有View對應的Behavoir攔截了的,並且不是ACTION_DOWN的時候,其他View的Behavoir都會收到ACTION_CANCEL事件。
第40行第二個if如果沒有被攔截並且有Behavoir則調用Behavoir對應的函數onInterceptTouchEvent或者onTouchEvent函數。
CoordinatorLayout的onTouchEvent函數
@Override public boolean onTouchEvent(MotionEvent ev) { boolean handled = false; boolean cancelSuper = false; MotionEvent cancelEvent = null; final int action = MotionEventCompat.getActionMasked(ev); if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev, TYPE_ON_TOUCH))) { // Safe since performIntercept guarantees that // mBehaviorTouchView != null if it returns true final LayoutParams lp = (LayoutParams) mBehaviorTouchView.getLayoutParams(); final Behavior b = lp.getBehavior(); if (b != null) { handled = b.onTouchEvent(this, mBehaviorTouchView, ev); } } // Keep the super implementation correct if (mBehaviorTouchView == null) { handled |= super.onTouchEvent(ev); } else if (cancelSuper) { if (cancelEvent != null) { final long now = SystemClock.uptimeMillis(); cancelEvent = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); } super.onTouchEvent(cancelEvent); } if (!handled && action == MotionEvent.ACTION_DOWN) { } if (cancelEvent != null) { cancelEvent.recycle(); } if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { resetTouchBehaviors(); } return handled; }
CoordinatorLayout的onTouchEvent函數就是看下當前事件是不是有哪個View的Behavior感興趣。如果感興趣就給View的Behavior做處理,不敢興趣就給CoordinatorLayout自己處理。
這樣Behavior的onInterceptTouchEvent + onTouchEvent。兩個函數的調用也引導進去了。
Behavior的layoutDependsOn + onDependentViewChanged + onDependentViewRemoved
/** * child是否要依賴dependency * @param parent CoordinatorLayout * @param child 該Behavior對應的那個View * @param dependency 要檢查的View(child是否要依賴這個dependency) * @return true 依賴, false 不依賴 */ public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) { return false; } /** * 在layoutDependsOn返回true的基礎上之後,及時報告dependency的狀態變化 * @param parent CoordinatorLayout * @param child 該Behavior對應的那個View * @param dependency child依賴dependency * @return true 處理了, false 沒處理 */ public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) { return false; } /** * 在layoutDependsOn返回true的基礎上之後,報告dependency被移除了 * @param parent CoordinatorLayout * @param child 該Behavior對應的那個View * @param dependency child依賴dependency */ public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) { }
所有的源頭都在CoordinatorLayout類裡面
CoordinatorLayout類裡面onAttachedToWindow
@Override public void onAttachedToWindow() { super.onAttachedToWindow(); resetTouchBehaviors(); if (mNeedsPreDrawListener) { if (mOnPreDrawListener == null) { mOnPreDrawListener = new OnPreDrawListener(); } final ViewTreeObserver vto = getViewTreeObserver(); vto.addOnPreDrawListener(mOnPreDrawListener); } if (mLastInsets == null && ViewCompat.getFitsSystemWindows(this)) { // We're set to fitSystemWindows but we haven't had any insets yet... // We should request a new dispatch of window insets ViewCompat.requestApplyInsets(this); } mIsAttachedToWindow = true; }
第10行給ViewTreeObserver添加了一個OnPreDrawListener的監聽。OnPreDrawListener代碼如下
class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener { @Override public boolean onPreDraw() { dispatchOnDependentViewChanged(false); return true; } }
OnPreDrawListener實現了ViewTreeObserver.OnPreDrawListener接口重寫了onPreDraw函數,onPreDraw在每次draw的時候都會調用。就是在CoordinatorLayout每次重繪的時候調用。View狀態發生變化的時候調用。這樣重點就到了dispatchOnDependentViewChanged函數了參數false表示不是嵌套滑動引起的變化是View狀態改變引起的變化。
void dispatchOnDependentViewChanged(final boolean fromNestedScroll) { final int layoutDirection = ViewCompat.getLayoutDirection(this); final int childCount = mDependencySortedChildren.size(); for (int i = 0; i < childCount; i++) { final View child = mDependencySortedChildren.get(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); // Check child views before for anchor for (int j = 0; j < i; j++) { final View checkChild = mDependencySortedChildren.get(j); if (lp.mAnchorDirectChild == checkChild) { offsetChildToAnchor(child, layoutDirection); } } // Did it change? if not continue final Rect oldRect = mTempRect1; final Rect newRect = mTempRect2; getLastChildRect(child, oldRect); getChildRect(child, true, newRect); if (oldRect.equals(newRect)) { continue; } recordLastChildRect(child, newRect); // Update any behavior-dependent views for the change for (int j = i + 1; j < childCount; j++) { final View checkChild = mDependencySortedChildren.get(j); final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams(); final Behavior b = checkLp.getBehavior(); if (b != null && b.layoutDependsOn(this, checkChild, child)) { if (!fromNestedScroll && checkLp.getChangedAfterNestedScroll()) { // If this is not from a nested scroll and we have already been changed // from a nested scroll, skip the dispatch and reset the flag checkLp.resetChangedAfterNestedScroll(); continue; } final boolean handled = b.onDependentViewChanged(this, checkChild, child); if (fromNestedScroll) { // If this is from a nested scroll, set the flag so that we may skip // any resulting onPreDraw dispatch (if needed) checkLp.setChangedAfterNestedScroll(handled); } } } } }
第3行 mDependencySortedChildren裡面放的是CoordinatorLayout所有的子View,只不過把相關連的View放到一起了(相關聯有兩種一種是直接xml裡面設置了layout_anchor,一種是Behavior裡面設置了關聯layoutDependsOn函數)。接著遍歷所有的View。
第9行到15行找到哪個View設置了layout_anchor也就是說設置顯示坐標的錨點 哪個View的layout_anchor對應child。調整位置,這個應該好理解點layout_anchor設置的View和當前的View是相關會一起變化的。
第28行到49行找到哪個View的Behavior depend on child這個View。第0行調用了Behavior的layoutDependsOn判斷是否依賴。如果依賴繼續調用Behavior的onDependentViewChanged函數。
到這裡Behavior的layoutDependsOn和onDependentViewChanged的調用的地方和調用的時機我們都知道了。還差一個onDependentViewRemoved函數。繼續看CoordinatorLayout裡面的HierarchyChangeListener裡 在哪裡用到了呢構造函數裡面super.setOnHierarchyChangeListener(new HierarchyChangeListener());設置了去監聽ViewGroup中的View的層次變化當View removed掉的時候這裡能夠監聽到了會調用onChildViewRemoved。
final class HierarchyChangeListener implements OnHierarchyChangeListener { @Override public void onChildViewAdded(View parent, View child) { if (mOnHierarchyChangeListener != null) { mOnHierarchyChangeListener.onChildViewAdded(parent, child); } } @Override public void onChildViewRemoved(View parent, View child) { dispatchDependentViewRemoved(child); if (mOnHierarchyChangeListener != null) { mOnHierarchyChangeListener.onChildViewRemoved(parent, child); } } }
繼續dispatchDependentViewRemoved函數。
void dispatchDependentViewRemoved(View removedChild) { final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final Behavior b = lp.getBehavior(); if (b != null && b.layoutDependsOn(this, child, removedChild)) { b.onDependentViewRemoved(this, child, removedChild); } } }
找到對應View的Behavior 如果是layoutDependsOn的就去調用Behavior的onDependentViewRemoved。
到此Behavior的layoutDependsOn + onDependentViewChanged + onDependentViewRemoved三個函數的調用時機和調用順序都結束了。
Behavior的onStartNestedScroll + onNestedScrollAccepted + onStopNestedScroll + onNestedScroll + onNestedPreScroll + onNestedFling + onNestedPreFling
/** * 有嵌套滑動到來了,問下該Behavior是否接受嵌套滑動 * * @param coordinatorLayout CoordinatorLayout * @param child 該Behavior對應的View * @param directTargetChild 嵌套滑動對應的父類的子類(因為嵌套滑動對於的父View不一定是一級就能找到的,可能挑了兩級父View的父View, directTargetChild>=target) * @param target 具體嵌套滑動的那個子類 * @param nestedScrollAxes 支持嵌套滾動軸。水平方向,垂直方向,或者不指定 * @return 是否接受該嵌套滑動 */ public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { return false; } /** * Behavior接受了嵌套滑動的請求該函數調用。onStartNestedScroll返回true該函數會被調用。 參數和onStartNestedScroll一樣 */ public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child, View directTargetChild, View target, int nestedScrollAxes) { // Do nothing } /** * 停止嵌套滑動 * * @param coordinatorLayout CoordinatorLayout * @param child 該Behavior對應的View * @param target 具體嵌套滑動的那個子類 */ public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { // Do nothing } /** * 嵌套滑動的子View在滑動之後報告過來的滑動情況 * * @param coordinatorLayout CoordinatorLayout * @param child 該Behavior對應的View * @param target 具體嵌套滑動的那個子類 * @param dxConsumed 水平方向嵌套滑動的子View滑動的距離(消耗的距離) * @param dyConsumed 垂直方向嵌套滑動的子View滑動的距離(消耗的距離) * @param dxUnconsumed 水平方向嵌套滑動的子View未滑動的距離(未消耗的距離) * @param dyUnconsumed 垂直方向嵌套滑動的子View未滑動的距離(未消耗的距離) */ public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { // Do nothing } /** * 在嵌套滑動的子View未滑動之前告訴過來的准備滑動的情況 * * @param coordinatorLayout CoordinatorLayout * @param child 該Behavior對應的View * @param target 具體嵌套滑動的那個子類 * @param dx 水平方向嵌套滑動的子View想要變化的距離 * @param dy 垂直方向嵌套滑動的子View想要變化的距離 * @param consumed 這個參數要我們在實現這個函數的時候指定,回頭告訴子View當前父View消耗的距離 consumed[0] 水平消耗的距離,consumed[1] 垂直消耗的距離 好讓子view做出相應的調整 */ public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed) { // Do nothing } /** * 嵌套滑動的子View在fling之後報告過來的fling情況 * * @param coordinatorLayout CoordinatorLayout * @param child 該Behavior對應的View * @param target 具體嵌套滑動的那個子類 * @param velocityX 水平方向速度 * @param velocityY 垂直方向速度 * @param consumed 子view是否fling了 * @return true Behavior是否消耗了fling */ public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY, boolean consumed) { return false; } /** * 在嵌套滑動的子View未fling之前告訴過來的准備fling的情況 * * @param coordinatorLayout CoordinatorLayout * @param child 該Behavior對應的View * @param target 具體嵌套滑動的那個子類 * @param velocityX 水平方向速度 * @param velocityY 垂直方向速度 * @return true Behavior是否消耗了fling */ public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, float velocityX, float velocityY) { return false; }
嵌套滑動引起的變化,根據前一篇文章Android 嵌套滑動分析的分析CoordinatorLayout 實現了 NestedScrollingParent,有嵌套滑動的時候會調用到CoordinatorLayout裡面的onStartNestedScroll onNestedScrollAccepted onStopNestedScroll onNestedScroll onNestedPreScroll onNestedFling onNestedPreFling這些函數。至於是怎麼調用到的這些函數可以看看Android 嵌套滑動分析的介紹。這些函數裡面做的事情也都是大同小異的都是直接過度給了Behavior裡面對應的函數。
這樣我們就知道了Behavior的onStartNestedScroll + onNestedScrollAccepted + onStopNestedScroll + onNestedScroll + onNestedPreScroll + onNestedFling + onNestedPreFling。的調用時機是有嵌套滑動的時候會被調用到。
AssetManager是android的資源管理器,負責管理android系統所有的資源.資源可以分系統級別和應用級別.系統級別主要是f
最近一直在學習自定義View相關的知識,今天給大家帶來的是QQ健康界面的實現。先看效果圖:可以設置數字顏色,字體顏色,運動步數,運動排名,運動平均步數,虛線下方的藍色指示
最近在做上傳文件的服務,簡單看了網上的教程。結合實踐共享出代碼。由於網上的大多數沒有服務端的代碼,這可不行呀,沒服務端怎麼調試呢。Ok,先上代碼。Android 上傳比較
今天我們來接觸一下多線程下載,當然也包括斷點續傳,我們可以看到很多下載器,當開通會員的時候下載東西的速度就變得快了許多,這是為什麼呢?這就是跟今天講的多線程有關系了,其實