版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、精品文檔View 繪制流程第一步:遞歸measure 源碼分析/final方法,子類不可重寫public final void measure(int widthMeasureSpec, intheightMeasureSpec) ./ 回調(diào) onMeasure()方法onMeasure(widthMeasureSpec, heightMeasureSpec);這個(gè)方法的兩個(gè)參數(shù)都是父 View 傳遞過來的,代表了父 view 的規(guī)格。他由兩部分組成,高 2 位表示 MODE,低 30 位表示 size 。/View的 onMeasure 默認(rèn)實(shí)現(xiàn)方法protected void onMeas
2、ure(int widthMeasureSpec,int heightMeasureSpec) setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);對(duì)于非 ViewGroup的 View 而言,通過調(diào)用上面默認(rèn)的onMeasure即可完成 View 的測量。 setMeasuredDimension函數(shù)是一個(gè)很關(guān)鍵的函數(shù),它完成了對(duì)View 的成員變量 mMe
3、asuredWidth和 mMeasuredHeight變量賦值。精品文檔精品文檔public static int getDefaultSize(int size, int measureSpec) int result = size;/ 通過 MeasureSpec解析獲取 mode 與 sizeint specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) case MeasureSpec.UNSPECIFIED:resul
4、t = size;break;case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result = specSize;break;return result;protected int getSuggestedMinimumWidth() return (mBackground = null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth();protected int getSuggestedMinimumHeight() return (mBackground = null)
5、 ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight();在 ViewGroup 中定義了 measureChildren, measureChild, measureChildWith-Margins 方法來對(duì)子視圖進(jìn)行測量, measureChildren 內(nèi)部實(shí)質(zhì)只是循環(huán)調(diào)用 measureChild 。protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMe
6、asureSpec, int heightUsed) / 獲取子視圖的 LayoutParamsfinal MarginLayoutParams lp = (MarginLayoutParams)child.getLayoutParams();/ 調(diào)整 MeasureSpec/ 通過這兩個(gè)參數(shù)以及子視圖本身LayoutParams 來共同決定子視圖的測量規(guī)格final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMarg
7、in + lp.rightMargin+ widthUsed, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin精品文檔精品文檔+ heightUsed, lp.height);/ 調(diào)運(yùn)子 View 的 measure 方法,子 View 的 measure 中會(huì)回調(diào)子 View 的/onMeasure方法child.measure(childW
8、idthMeasureSpec, childHeightMeasureSpec);該方法就是對(duì)父視圖提供的measureSpec參數(shù)結(jié)合自身的LayoutParams參數(shù)進(jìn)行了調(diào)整,然后再來調(diào)用child.measure()方法,具體通過方法getChildMeasureSpec來進(jìn)行參數(shù)調(diào)整。public static int getChildMeasureSpec(int spec, int padding, intchildDimension) /獲取當(dāng)前 Parent View的 Mode 和 Sizeint specMode = MeasureSpec.getMode(spec);i
9、nt specSize = MeasureSpec.getSize(spec);/獲取 Parentsize與 padding差值(也就是Parent剩余大?。?,若差值小于0直接返回 0int size = Math.max(0, specSize - padding);/ 定義返回值存儲(chǔ)變量int resultSize = 0;int resultMode = 0;/ 依據(jù)當(dāng)前 Parent 的 Mode 進(jìn)行 switch 分支邏輯switch (specMode) / Parent has imposed an exact size on us/ 默認(rèn) Root View 的 Mode
10、就是 EXACTLY case MeasureSpec.EXACTLY:if (childDimension >= 0) /如果 child的 layout_wOrh屬性在 xml 或者 java中給予具體大/ 于等于 0 的數(shù)值/設(shè)置 child的 size 為真實(shí) layout_wOrh 屬性值,mode 為 EXACTLYresultSize = childDimension;resultMode = MeasureSpec.EXACTLY; else if (childDimension = LayoutParams.MATCH_PARENT) /如果 child的 layout
11、_wOrh屬性在 xml 或者 java中給予/MATCH_PARENT/ Child wants to be our size. So be it./設(shè)置 child的 size 為 size , mode 為 EXACTLYresultSize = size;resultMode = MeasureSpec.EXACTLY; else if (childDimension = LayoutParams.WRAP_CONTENT) /如果 child的 layout_wOrh屬性在 xml 或者 java中給予/WRAP_CONTENT/設(shè)置 child的 size 為 size , mod
12、e 為 AT_MOST/ Child wants to determine its own size. It can't be/ bigger than us.resultSize = size;精品文檔精品文檔resultMode = MeasureSpec.AT_MOST;break;./ 其他 Mode 分支類似/ 將 mode 與 size 通過 MeasureSpec 方法整合為 32 位整數(shù)返回return MeasureSpec.makeMeasureSpec(resultSize, resultMode);·用 View 的 getMeasuredWidth(
13、)和 getMeasuredHeight()方法來獲取View 測量的寬高,必須保證這兩個(gè)方法在onMeasure 流程之后被調(diào)用才能返回有效值。·MeasureSpec (View 的內(nèi)部類)測量規(guī)格為int型,值由高 2 位規(guī)格模式specMode和低 30 位具體尺寸specSize組成。其中specMode 只有三種值:MeasureSpec.EXACTLY /確定模式,父View 希望子 View 的大小是確定的,由specSize決定;MeasureSpec.AT_MOST / 最多模式,父 View 希望子 View 的大小最多是 specSize指定的值;Measur
14、eSpec.UNSPECIFIED / 未指定模式,父 View 完全依據(jù)子 View 的設(shè)計(jì)值來決定;View 繪制流程第二步:遞歸layout源碼分析精品文檔精品文檔ViewGroup的 layout方法,如下:Overridepublic final void layout(int l, int t, int r, int b) .super.layout(l, t, r, b);.調(diào)運(yùn)了 View 父類的 layout方法,所以我們看下View 的 layout源碼,如下:public void layout(int l, int t, int r, int b) / 實(shí)質(zhì)都是調(diào)用 s
15、etFrame 方法把參數(shù)分別賦值給 mLeft 、 mTop、 mRight 和 /mBottom 這幾個(gè)變量/ 判斷 View 的位置是否發(fā)生過變化,以確定有沒有必要對(duì)當(dāng)前的View 進(jìn)行重新/layoutboolean changed = isLayoutModeOptical(mParent) ?setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);if (changed | (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) =PFLAG_LAYOUT_REQUIRED) onLayout(chang
16、ed, l, t, r, b);/ViewGroup的 onLayout方法,如下:Overrideprotected abstract void onLayout(boolean changed,int l, int t, int r, int b);關(guān)于 getWidth()、getHeight()和 getMeasuredWidth()、getMeasuredHeight()這兩對(duì)方法之間的區(qū)別public final int getMeasuredWidth () return mMeasuredWidth & MEASURED_SIZE_MASK;public final i
17、nt getMeasuredHeight() return mMeasuredHeight & MEASURED_SIZE_MASK;public final int getWidth() return mRight - mLeft;public final int getHeight() return mBottom - mTop;精品文檔精品文檔·View.layout方 法 可 被 重 載 , ViewGroup.layout為final的 不 可 重 載 ,ViewGroup.onLayout為 abstract的,子類必須重載實(shí)現(xiàn)自己的位置邏輯。·凡是 l
18、ayout_XXX的布局屬性基本都針對(duì)的是包含子View 的 ViewGroup的,當(dāng)對(duì)一個(gè)沒有父容器的View 設(shè)置相關(guān) layout_XXX屬性是沒有任何意義的·使用 View 的 getWidth()和 getHeight()方法來獲取View 測量的寬高, 必須保證這兩個(gè)方法在onLayout流程之后被調(diào)用才能返回有效值View 繪制流程第三步:遞歸draw 源碼分析ViewGroup沒有重寫 View 的 draw 方法,所以如下直接從View 的 draw 方法開始public void draw(Canvas canvas) / Step 1, draw the bac
19、kground, if needed if (!dirtyOpaque) drawBackground(canvas);/ skip step 2 & 5 if possible (common case)/ Step 2, save the canvas' layersif (drawTop) canvas.saveLayer(left, top, right, top + length, null, flags);/ Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);/ Step 4, draw the chi
20、ldren dispatchDraw(canvas);/ Step 5, draw the fade effect and restore layers if (drawTop) matrix.setScale(1, fadeHeight * topFadeStrength);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);canvas.drawRect(left, top, right, top + length, p);/ Step 6, draw decorations (scro
21、llbars) onDrawScrollBars(canvas);精品文檔精品文檔private void drawBackground(Canvas canvas) / 獲取 xml 中通過 android:background屬性或者代碼中/setBackgroundColor()、 setBackgroundResource()等方法進(jìn)行賦值的背景/Drawablefinal Drawable background = mBackground;./ 根據(jù) layout 過程確定的 View 位置來設(shè)置背景的繪制區(qū)域if (mBackgroundSizeChanged) backgroun
22、d.setBounds(0, 0, mRight - mLeft, mBottom - mTop);mBackgroundSizeChanged = false;rebuildOutline();./ 調(diào)用 Drawable 的 draw() 方法來完成背景的繪制工作background.draw(canvas);./View的 onDraw 方法 ,這是一個(gè)空方法。因?yàn)槊總€(gè)View 的內(nèi)容部分是各不相同的,/ 所以需要由子類去實(shí)現(xiàn)具體邏輯。protected void onDraw(Canvas canvas) / View的 dispatchDraw()方法是一個(gè)空方法,如果View 包含
23、子類需要重寫他,所/ 以我們有必要看下ViewGroup的 dispatchDraw方法源碼Overrideprotected void dispatchDraw(Canvas canvas) .final int childrenCount = mChildrenCount;final View children = mChildren;for (int i = 0; i < childrenCount; i+) if (child.mViewFlags & VISIBILITY_MASK) = VISIBLE |child.getAnimation() != null) mo
24、re |= drawChild(canvas, child, drawingTime);/ Draw any disappearing views that have animationsif (mDisappearingChildren != null) for (int i = disappearingCount; i >= 0; i-) more |= drawChild(canvas, child, drawingTime);精品文檔精品文檔/ ViewGroup確實(shí)重寫了View 的 dispatchDraw()方法,該方法內(nèi)部會(huì)遍歷每個(gè)子/View,然后調(diào)用drawChild
25、()方法,我們可以看下ViewGroup的 drawChild方法protected boolean drawChild(Canvas canvas, View child,long drawingTime) return child.draw(canvas, this, drawingTime);drawChild()方法調(diào)運(yùn)了子View 的 draw() 方法。所以說ViewGroup類已經(jīng)為我們重寫了 dispatchDraw() 的功能實(shí)現(xiàn),我們一般不需要重寫該方法,但可以重載父類函數(shù)實(shí)現(xiàn)具體的功能。·在獲取畫布剪切區(qū)時(shí)會(huì)自動(dòng)處理掉 padding ,子 View 獲取 Ca
26、nvas 不用關(guān)注這些邏輯,只用關(guān)心如何繪制即可。·默認(rèn)情況下子 View 的 ViewGroup.drawChild繪制順序和子View 被添加的順序一致,但是你也可以重載ViewGroup.getChildDrawingOrder()方法提供不同順序。View 的 invalidate方法源碼分析View 類中的一些invalidate方法/This must be called from a UI thread. To call from a non-UI thread,/ call postInvalidate()public void invalidate(Rect dir
27、ty) final int scrollX = mScrollX;final int scrollY = mScrollY;/實(shí)質(zhì)還是調(diào)運(yùn)invalidateInternal方法invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,dirty.right- scrollX,dirty.bottom- scrollY,true,false);/This must be called from a UI thread. To call from a non-UI thread,/ call postInvalidate()publ
28、ic void invalidate(int l, int t, int r, int b) final int scrollX = mScrollX;final int scrollY = mScrollY;/實(shí)質(zhì)還是調(diào)運(yùn)invalidateInternal方法invalidateInternal(l- scrollX,t - scrollY,r - scrollX,b - scrollY,true, false);/This must be called from a UI thread. To call from a non-UI thread,/ call postInvalidate
29、() public void invalidate() 精品文檔精品文檔/invalidate的實(shí)質(zhì)還是調(diào)運(yùn)invalidateInternal方法invalidate(true);/thisfunctioncan be calledwithinvalidateCachesettofalseto/skipthat invalidation stepvoid invalidate(boolean invalidateCache) /實(shí)質(zhì)還是調(diào)運(yùn) invalidateInternal 方法 invalidateInternal(0, 0, mRight - mLeft, mBottom - mTo
30、p,invalidateCache, true);/ 所有 invalidate的最終調(diào)運(yùn)方法voidinvalidateInternal(intl,intt,intr,intb,booleaninvalidateCache, boolean fullInvalidate) ./ Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo;final ViewParent p = mParent;if (p != null && ai != null &&
31、; l < r && t < b) final Rect damage = ai.mTmpInvalRect;/ 設(shè)置刷新區(qū)域damage.set(l, t, r, b);/ 傳遞調(diào)運(yùn) Parent ViewGroup 的 invalidateChild 方法 p.invalidateChild(this, damage);.View 的 invalidate(invalidateInternal)方法實(shí)質(zhì)是將要刷新區(qū)域直接傳遞給了父 ViewGroup的 invalidateChild方法,在invalidate中,調(diào)用父View的invalidateChild,
32、這是一個(gè)從當(dāng)前向上級(jí)父View 回溯的過程ViewGroup的 invalidateChild方法public final void invalidateChild(View child, final Rect dirty) ViewParent parent = this;final AttachInfo attachInfo = mAttachInfo;.do / 循環(huán)層層上級(jí)調(diào)運(yùn),直到 ViewRootImpl 會(huì)返回 null parent = parent.invalidateChildInParent(location, dirty); while (parent != null)
33、;精品文檔精品文檔最后傳遞到ViewRootImpl的 invalidateChildInParent方法結(jié)束,所以我們看下ViewRootImpl的 invalidateChildInParent方法Overridepublic ViewParent invalidateChildInParent(int location,Rect dirty)./View調(diào)運(yùn) invalidate最終層層上傳到ViewRootImpl后最終觸發(fā)了該方法scheduleTraversals();.return null;這個(gè) ViewRootImpl類的 invalidateChildInParent方法直
34、接返回了null,結(jié)束了那個(gè) do while循環(huán)。 scheduleTraversals會(huì)通過 Handler的 Runnable發(fā)送一個(gè)異步消息,調(diào)運(yùn)doTraversal方法,然后最終調(diào)用performTraversals()執(zhí)行重繪。所 以說View調(diào)運(yùn)invalidate方 法的實(shí) 質(zhì)是 層層上 傳到 父級(jí), 直到 傳遞到ViewRootImpl后觸發(fā)了 scheduleTraversals方法,然后整個(gè) View 樹開始重新按照上面分析的View 繪制流程進(jìn)行重繪任務(wù)。View 的 postInvalidate方法源碼分析invalidate方法只能在UI Thread中執(zhí)行,其他
35、線程中需要使用postInvalidate方法public void postInvalidate() postInvalidateDelayed(0);public void postInvalidateDelayed(long delayMilliseconds) final AttachInfo attachInfo = mAttachInfo;/ 核心,實(shí)質(zhì)就是調(diào)運(yùn)了 ViewRootImpl.dispatchInvalidateDelayed 方法 if (attachInfo != null) delayMilliseconds);public void dispatchInval
36、idateDelayed(View view,long delayMilliseconds) Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); mHandler.sendMessageDelayed(msg, delayMilliseconds);調(diào)運(yùn)的 ViewRootImpl類的 dispatchInvalidateDelayed方法 ,通過 ViewRootImpl類的 Handler發(fā)送了一條 MSG_INVALIDATE消息 ,繼續(xù)追蹤這條消息的處理可以發(fā)現(xiàn):精品文檔精品文檔public void handleMessage(Message msg) .switch (msg.what) case MSG_INVALIDATE:(View)msg.obj).invalidate();break;.invalidate系列方法請(qǐng)求重繪View 樹(也就是draw 方法),如果 View 大小沒有發(fā)生變化就不會(huì)調(diào)用layout過程,并且只繪制那些“需要重繪的”View ,也就是哪個(gè)V
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 皮革漂白制劑市場發(fā)展前景分析及供需格局研究預(yù)測報(bào)告
- 手動(dòng)螺旋切菜器產(chǎn)品供應(yīng)鏈分析
- 多媒體圖書館服務(wù)行業(yè)營銷策略方案
- 發(fā)行預(yù)付費(fèi)電話卡行業(yè)相關(guān)項(xiàng)目經(jīng)營管理報(bào)告
- 修腳時(shí)穿的泡沫拖鞋產(chǎn)業(yè)鏈招商引資的調(diào)研報(bào)告
- 擴(kuò)音器用變送器產(chǎn)業(yè)鏈招商引資的調(diào)研報(bào)告
- 3.2遵守規(guī)則 同步課件 -2024-2025學(xué)年統(tǒng)編版道德與法治八年級(jí)上冊(cè)
- 自動(dòng)駕駛送貨機(jī)器人項(xiàng)目營銷計(jì)劃書
- 廣告咨詢行業(yè)相關(guān)項(xiàng)目經(jīng)營管理報(bào)告
- 創(chuàng)建設(shè)計(jì)和維護(hù)網(wǎng)站行業(yè)經(jīng)營分析報(bào)告
- 2022年度食品安全負(fù)責(zé)人考試題庫(含答案)
- 教師近3年任教學(xué)科學(xué)生學(xué)業(yè)水平和綜合素質(zhì)
- 企業(yè)法律合規(guī)與外部監(jiān)管的內(nèi)外因素分析
- 2022年版煤礦安全規(guī)程
- 九年級(jí)數(shù)學(xué)上冊(cè) 期中考試卷(湘教版)
- 冷彎機(jī)行業(yè)市場研究報(bào)告
- 牛津英語四年級(jí)上冊(cè)4A-M2-Unit-3-The-lion-and-the-mouse優(yōu)秀信息化教案附反思
- 個(gè)人住房貸款提前還款月供及節(jié)省利息EXCEL計(jì)算
- 山東省青島市膠州市2023-2024學(xué)年八年級(jí)上學(xué)期期中英語試卷
- 第三單元“閱讀策略”(主題閱讀) 六年級(jí)語文上冊(cè)閱讀理解(統(tǒng)編版)
- 河北省保定市定州市2023-2024學(xué)年六年級(jí)上學(xué)期期中質(zhì)量監(jiān)測科學(xué)測試卷
評(píng)論
0/150
提交評(píng)論