Android View刷新機(jī)制實(shí)例分析
本文實(shí)例講述了Android View刷新機(jī)制。分享給大家供大家參考,具體如下:
一、總體說(shuō)明
在Android的布局體系中,父View負(fù)責(zé)刷新、布局顯示子View;而當(dāng)子View需要刷新時(shí),則是通知父View來(lái)完成。
二、代碼分析
1).ViewGroup的addView方法,理解參數(shù)的意義和傳遞
invalidate調(diào)用父類View的方法
addViewInner方法主要做的事情是
view的dispatchAttachedToWindow(AttachInfo info, int visibility)方法
1).View的invalidate方法,這是一個(gè)從下第向上回溯的過(guò)程,每一層的父View都將自己的顯示區(qū)域與傳入的刷新
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);//調(diào)用子類的方法完成
}
}
}
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) {
// 根據(jù)父View的位置,偏移刷新區(qū)域
dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY);
final int left = mLeft;
final int top = mTop;
//計(jì)算實(shí)際可刷新區(qū)域
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;
}
這個(gè)向上回溯的過(guò)程直到ViewRoot那里結(jié)束,由ViewRoot對(duì)這個(gè)最終的刷新區(qū)域做刷新
ViewRoot.java
public void invalidateChild(View child, Rect dirty) {
}
由ViewRoot對(duì)象的performTraversals()方法調(diào)用draw()方法發(fā)起繪制該View樹(shù),值得注意的是每次發(fā)起繪圖時(shí),并不會(huì)重新繪制每個(gè)View樹(shù)的視圖,而只會(huì)重新繪制那些“需要重繪”的視圖,View類內(nèi)部變量包含了一個(gè)標(biāo)志位DRAWN,當(dāng)該視圖需要重繪時(shí),就會(huì)為該View添加該標(biāo)志位。
調(diào)用流程 :
mView.draw()開(kāi)始繪制,draw()方法實(shí)現(xiàn)的功能如下:
1 、繪制該View的背景
2 、為顯示漸變框做一些準(zhǔn)備操作(見(jiàn)5,大多數(shù)情況下,不需要改漸變框)
3、調(diào)用onDraw()方法繪制視圖本身 (每個(gè)View都需要重載該方法,ViewGroup不需要實(shí)現(xiàn)該方法)
4、調(diào)用dispatchDraw ()方法繪制子視圖(如果該View類型不為ViewGroup,即不包含子視圖,不需要重載該
方法)值得說(shuō)明的是,ViewGroup類已經(jīng)為我們重寫(xiě)了dispatchDraw ()的功能實(shí)現(xiàn),應(yīng)用程序一般不需要重寫(xiě)該
方法,但可以重載父類函數(shù)實(shí)現(xiàn)具體的功能。
4.1 dispatchDraw()方法內(nèi)部會(huì)遍歷每個(gè)子視圖,調(diào)用drawChild()去重新回調(diào)每個(gè)子視圖的draw()方法(注意,這個(gè) 地方“需要重繪”的視圖才會(huì)調(diào)用draw()方法)。值得說(shuō)明的是,ViewGroup類已經(jīng)為我們重寫(xiě)了dispatch
Draw()的功能實(shí)現(xiàn),應(yīng)用程序一般不需要重寫(xiě)該方法,但可以重載父類函數(shù)實(shí)現(xiàn)具體的功能。
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android開(kāi)發(fā)入門(mén)與進(jìn)階教程》、《Android多媒體操作技巧匯總(音頻,視頻,錄音等)》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
- Android實(shí)現(xiàn)上拉加載更多以及下拉刷新功能(ListView)
- Android開(kāi)發(fā)之ListView實(shí)現(xiàn)Item局部刷新
- Android中ListView下拉刷新的實(shí)現(xiàn)方法實(shí)例分析
- Android開(kāi)發(fā)之ListView列表刷新和加載更多實(shí)現(xiàn)方法
- android中ListView數(shù)據(jù)刷新時(shí)的同步方法
- android開(kāi)發(fā)教程之實(shí)現(xiàn)listview下拉刷新和上拉刷新效果
- android中ListView多次刷新重復(fù)執(zhí)行g(shù)etView的解決方法
- Android開(kāi)發(fā)筆記之:ListView刷新順序的問(wèn)題詳解
- android下拉刷新ListView的介紹和實(shí)現(xiàn)代碼
- Android下拉刷新ListView——RTPullListView(demo)
相關(guān)文章
RadioGroup實(shí)現(xiàn)單選框的多行排列
這篇文章主要為大家詳細(xì)介紹了RadioGroup實(shí)現(xiàn)單選框的多行排列,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android 自定義圓形帶刻度漸變色的進(jìn)度條樣式實(shí)例代碼
這篇文章主要介紹了Android 自定義圓形帶刻度漸變色的進(jìn)度條的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
Android中NavigationView的使用與相關(guān)問(wèn)題解決
大家都知道NavigationView的引入讓 Android側(cè)邊欄實(shí)現(xiàn)起來(lái)相當(dāng)方便,最近公司項(xiàng)目中也使用這個(gè)新的控件完成了側(cè)邊欄的改版。在使用過(guò)程中遇到一些問(wèn)題所以記錄一下。本文分為兩個(gè)部分,一是基本使用,二是相關(guān)問(wèn)題的解決,感興趣的朋友們下面來(lái)一起看看吧。2016-10-10
Android App中實(shí)現(xiàn)簡(jiǎn)單的刮刮卡抽獎(jiǎng)效果的實(shí)例詳解
這篇文章主要介紹了Android App中實(shí)現(xiàn)簡(jiǎn)單的刮刮卡抽獎(jiǎng)效果的實(shí)例詳解,文中主要借助Bitmap的canvas.drawPath的api來(lái)實(shí)現(xiàn),需要的朋友可以參考下2016-03-03
Android動(dòng)態(tài)時(shí)鐘壁紙開(kāi)發(fā)
這篇文章主要為大家詳細(xì)介紹了Android動(dòng)態(tài)時(shí)鐘壁紙開(kāi)發(fā)的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
Android EditText 監(jiān)聽(tīng)用戶輸入完成的實(shí)例
下面小編就為大家分享一篇Android EditText 監(jiān)聽(tīng)用戶輸入完成的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Android6.0獲取動(dòng)態(tài)權(quán)限代碼示例
這篇文章主要介紹了Android6.0以上獲取動(dòng)態(tài)權(quán)限代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11

