編輯:關於Android編程
本文實例講述了Android View刷新機制。分享給大家供大家參考,具體如下:
一、總體說明
在Android的布局體系中,父View負責刷新、布局顯示子View;而當子View需要刷新時,則是通知父View來完成。
二、代碼分析
1).ViewGroup的addView方法,理解參數的意義和傳遞
invalidate調用父類View的方法
addViewInner方法主要做的事情是
view的dispatchAttachedToWindow(AttachInfo info, int visibility)方法
1).View的invalidate方法,這是一個從下第向上回溯的過程,每一層的父View都將自己的顯示區域與傳入的刷新
Rect做交集。
void invalidate(boolean invalidateCache) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); } if (skipInvalidate()) { return; } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) || (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) { mLastIsOpaque = isOpaque(); mPrivateFlags &= ~DRAWN; mPrivateFlags |= DIRTY; if (invalidateCache) { mPrivateFlags |= INVALIDATED; mPrivateFlags &= ~DRAWING_CACHE_VALID; } final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; //noinspection PointlessBooleanExpression,ConstantConditions if (!HardwareRenderer.RENDER_DIRTY_REGIONS) { if (p != null && ai != null && ai.mHardwareAccelerated) { // fast-track for GL-enabled applications; just invalidate the whole hierarchy // with a null dirty rect, which tells the ViewAncestor to redraw everything p.invalidateChild(this, null); return; } } if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); // Don't call invalidate -- we don't want to internally scroll // our own bounds p.invalidateChild(this, r);//調用子類的方法完成 } } }
2)ViewGrop的invalidateChild方法
public final void invalidateChild(View child, final Rect dirty) { ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { final int[] location = attachInfo.mInvalidateChildLocation; // 需要刷新的子View的位置 location[CHILD_LEFT_INDEX] = child.mLeft; location[CHILD_TOP_INDEX] = child.mTop; // If the child is drawing an animation, we want to copy this flag onto // ourselves and the parent to make sure the invalidate request goes through final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION; // Check whether the child that requests the invalidate is fully opaque final boolean isOpaque = child.isOpaque() && !drawAnimation && child.getAnimation() != null; // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY; do { View view = null; if (parent instanceof View) { view = (View) parent; } if (drawAnimation) { if (view != null) { view.mPrivateFlags |= DRAW_ANIMATION; } else if (parent instanceof ViewRoot) { ((ViewRoot) parent).mIsAnimating = true; } } // If the parent is dirty opaque or not dirty, mark it dirty with the opaque // flag coming from the child that initiated the invalidate if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) { view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag; } parent = parent.invalidateChildInParent(location, dirty); } while (parent != null); } } public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) { if ((mPrivateFlags & DRAWN) == DRAWN) { if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != FLAG_OPTIMIZE_INVALIDATE) { // 根據父View的位置,偏移刷新區域 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY); final int left = mLeft; final int top = mTop; //計算實際可刷新區域 if (dirty.intersect(0, 0, mRight - left, mBottom - top) || (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) { mPrivateFlags &= ~DRAWING_CACHE_VALID; location[CHILD_LEFT_INDEX] = left; location[CHILD_TOP_INDEX] = top; return mParent; } } else { mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID; location[CHILD_LEFT_INDEX] = mLeft; location[CHILD_TOP_INDEX] = mTop; dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX], mBottom - location[CHILD_TOP_INDEX]); return mParent; } } return null; }
這個向上回溯的過程直到ViewRoot那裡結束,由ViewRoot對這個最終的刷新區域做刷新
ViewRoot.java
public void invalidateChild(View child, Rect dirty) { }
由ViewRoot對象的performTraversals()方法調用draw()方法發起繪制該View樹,值得注意的是每次發起繪圖時,並不會重新繪制每個View樹的視圖,而只會重新繪制那些“需要重繪”的視圖,View類內部變量包含了一個標志位DRAWN,當該視圖需要重繪時,就會為該View添加該標志位。
調用流程 :
mView.draw()開始繪制,draw()方法實現的功能如下:
1 、繪制該View的背景
2 、為顯示漸變框做一些准備操作(見5,大多數情況下,不需要改漸變框)
3、調用onDraw()方法繪制視圖本身 (每個View都需要重載該方法,ViewGroup不需要實現該方法)
4、調用dispatchDraw ()方法繪制子視圖(如果該View類型不為ViewGroup,即不包含子視圖,不需要重載該
方法)值得說明的是,ViewGroup類已經為我們重寫了dispatchDraw ()的功能實現,應用程序一般不需要重寫該
方法,但可以重載父類函數實現具體的功能。
4.1 dispatchDraw()方法內部會遍歷每個子視圖,調用drawChild()去重新回調每個子視圖的draw()方法(注意,這個 地方“需要重繪”的視圖才會調用draw()方法)。值得說明的是,ViewGroup類已經為我們重寫了dispatch
Draw()的功能實現,應用程序一般不需要重寫該方法,但可以重載父類函數實現具體的功能。
更多關於Android相關內容感興趣的讀者可查看本站專題:《Android開發入門與進階教程》、《Android多媒體操作技巧匯總(音頻,視頻,錄音等)》、《Android基本組件用法總結》、《Android視圖View技巧總結》、《Android布局layout技巧總結》及《Android控件用法總結》
希望本文所述對大家Android程序設計有所幫助。
Android的啟動過程是從init開始的,它是所有後續進程的祖進程。系統啟動的過程可以大致分為以下幾個步驟1,init.c的啟動 掛載目錄 初始化 解析配置文件2
你是qq火炬手嗎?在這個騰訊QQ為巴西裡約奧運會推出的火炬傳遞活動中你是第幾位火炬手了呢?QQ火炬好友火炬排行榜怎麼看?下面小編就來教教你!QQ火炬好友火炬
本文主要實現在自定義的ListView布局中加入CheckBox控件,通過判斷用戶是否選中CheckBox來對ListView的選中項進行相應的操作。通過一個Demo來展
Android中UI特效 android經典開源代碼分享 本文原始網址 作者為23code,歡迎給作者投稿 其它UI相關的網址: https://github.co