日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记

發(fā)布時(shí)間:2023/12/15 Android 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文鏈接 http://sparkyuan.me/ 轉(zhuǎn)載請注明出處

View的繪制流程是從ViewRoot的performTraversals方法開始的。它經(jīng)過measure、layout和draw三個(gè)過程才干終于將一個(gè)View繪制出來,當(dāng)中measure用來測量View的寬和高,layout用來確定View在父容器中的放置位置,而draw則負(fù)責(zé)將View繪制在屏幕上。

ViewRoot和DecorView

ViewRoot

  • ViewRoot相應(yīng)ViewRootImpl類,它是連接WindowManager和DecorView的紐帶,View的三大流程均通過ViewRoot來完畢。
  • ActivityThread中。Activity創(chuàng)建完畢后,會(huì)將DecorView加入到Window中,同一時(shí)候創(chuàng)建ViewRootImpl對象,并建立兩者的關(guān)聯(lián)。

DecorView

  • DecorView作為頂級View,普通情況下它內(nèi)部包括一個(gè)豎直方向的LinearLayout,在這個(gè)LinearLayout里面有上下兩個(gè)部分(詳細(xì)情況和Android版本號(hào)及主體有關(guān)),上面的是標(biāo)題欄,以下的是內(nèi)容欄。在Activity中通過setContentView所設(shè)置的布局文件事實(shí)上就是被加到內(nèi)容欄之中的,而內(nèi)容欄的id是content,在代碼中能夠通過ViewGroup content = (ViewGroup)findViewById(R.android.id.content)來得到content相應(yīng)的layout。
  • DecorView事實(shí)上是一個(gè)FrameLayout,View層的事件都先經(jīng)過DecorView。然后才傳遞給我們的View。

MeasureSpec

在測量過程中,系統(tǒng)會(huì)將View的LayoutParams依據(jù)父容器所施加的規(guī)則轉(zhuǎn)換成相應(yīng)的MeasureSpec。然后再依據(jù)這個(gè)MeasureSpec來測量出View的寬和高。測量出來的寬和高不一定等于View終于的寬和高。


MeasureSpec將SpecMode和SpecSize打包成一個(gè)int值來避免過多的對象內(nèi)存分配。高2位代表SpecMode,低30位代表SpecSize,SpecMode是指測量模式。而SpecSize是指在某種測量模式下的規(guī)格大小。

SpecMode有三類:

  • UNSPECIFIED:父容器不正確View有不論什么限制,要多大給多大。這樣的情況一般用于系統(tǒng)內(nèi)部。表示一種測量狀態(tài)
  • EXACTLY:父容器已經(jīng)檢測出View所須要的精確大小,這個(gè)時(shí)候View的終于大小就是SpecSize所指定的值。它相應(yīng)于LayoutParams中的match_parent和詳細(xì)的數(shù)值這兩種模式
  • AT_MOST:父容器指定了一個(gè)可用大小即SpecSize,View的大小不能大于這個(gè)值,詳細(xì)是什么值要看不同View的詳細(xì)實(shí)現(xiàn)。它相應(yīng)于LayoutParams中的wrap_content。

普通MeasureSpec的創(chuàng)建規(guī)則

對于普通View。其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams來共同決定。

  • 子View為精確寬高,不管父容器的MeasureSpec,子View的MeasureSpec都為精確值且遵循LayoutParams中的值。
  • 子View為match_parent時(shí),假設(shè)父容器是精確模式,則子View也為精確模式且為父容器的剩余空間大小;假設(shè)父容器是最大模式。則子View也是最大模式且不會(huì)超過父容器的剩余空間。
  • 子View為wrap_content時(shí)。不管父View是精確還是最大模式,子View的模式總是最大模式。且不會(huì)超過父容器的剩余空間。

View的工作流程

measure

ViewGroup的measure方法會(huì)遍歷每一個(gè)子元素,并調(diào)用子元素內(nèi)部的measure方法,measure源代碼例如以下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}

注:

  • getDefaultSize()返回MeasureSpec中的specSize,也就是View測量后的大小。
  • getSuggestedMinimumWidth(),View假設(shè)沒有背景,那么返回android:minWidth這個(gè)屬性指定的值,這個(gè)值能夠?yàn)?;假設(shè)設(shè)置了背景,則返回背景的最小寬度和minWidth中的最大值
  • getSuggestedMinimumHeight(),與getSuggestedMinimumWidth()相似。
  • 直接繼承View的自己定義控件須要重寫onMeasure方法并設(shè)置wrap_content時(shí)的自身大小,否則在布局中使用wrap_content時(shí)就相當(dāng)于使用match_parent。

    由于LayoutParams=wrap_content的情況下,MeasureSpec為AT_MOST。所以View的寬和高為父容器當(dāng)前剩余的空間,這樣的效果與match_parent一致。詳細(xì)處理方法要依據(jù)需求靈活決定。

怎樣得到View的寬和高

在Activity的onCreate、onStart、onResume方法中均無法正確得到某個(gè)View的寬/高信息。這是由于View的measure過程和Activity的生命周期方法不是同步運(yùn)行的,因此無法保證Activity運(yùn)行了onCreate、onStart、onResume時(shí)某個(gè)View就已經(jīng)測量完畢了,假設(shè)View還沒有測量完畢,那么獲得的寬/高就是0。

能夠通過例如以下四個(gè)方法來解決問題:

  • Activity或者View的onWindowFocusChanged方法(注意該方法會(huì)在Activity Pause和resume時(shí)被多次調(diào)用)
  • view.post(new Runnable( {@Overidde public void run(){})}),在run方法中獲取。
  • ViewTreeObserver中的onGlobalLayoutListener中。
  • 手動(dòng)調(diào)用View的measure方法。

演示樣例代碼請參考原書P190頁

layout

layout的作用是用來確定子視圖在父視圖中的位置。源代碼例如以下:

public void layout(int l, int t, int r, int b) { int oldL = mLeft; int oldT = mTop; int oldB = mBottom; int oldR = mRight; boolean changed = setFrame(l, t, r, b); if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT); } onLayout(changed, l, t, r, b); mPrivateFlags &= ~LAYOUT_REQUIRED; if (mOnLayoutChangeListeners != null) { ArrayList<OnLayoutChangeListener> listenersCopy = (ArrayList<OnLayoutChangeListener>) mOnLayoutChangeListeners.clone(); int numListeners = listenersCopy.size(); for (int i = 0; i < numListeners; ++i) { listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB); } } } mPrivateFlags &= ~FORCE_LAYOUT; }

通過setFrame()確定四個(gè)頂點(diǎn)的位置,進(jìn)而確定View在父容器中的位置。

在View的默認(rèn)實(shí)現(xiàn)中,View的測量寬/高和終于寬/高是相等的,僅僅只是測量寬/高形成于View的measure過程,而終于寬/高形成于View的layout過程。即兩者的賦值時(shí)機(jī)不同,測量寬/高的賦值時(shí)機(jī)略微早一些。多數(shù)情況下能夠覺得View的測量寬/高就等于終于的寬/高,但對于在View的layout中改變了View的left、top、right、bottom四個(gè)屬性時(shí),得出的測量寬/高有可能和終于的寬/高不一致。

draw

draw的過程非常easy主要有以下幾步:

  • 繪制背景(background.draw)
  • 繪制自己(onDraw)
  • 繪制children(dispatchDraw)
  • 繪制裝飾(onDrawScrollBars)。

源代碼例如以下

public void draw(Canvas canvas) { / * Draw traversal performs several drawing steps which must be executed * in the appropriate order: * * 1. Draw the background if need * 2. If necessary, save the canvas' layers to prepare for fading * 3. Draw view's content * 4. Draw children (dispatchDraw) * 5. If necessary, draw the fading edges and restore layers * 6. Draw decorations (scrollbars for instance) */ //Step 1, draw the background, if needed if (!dirtyOpaque) { drawBackground(canvas); } // skip step 2 & 5 if possible (common case) final int viewFlags = mViewFlags; if (!verticalEdges && !horizontalEdges) { // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); if (mOverlay != null && !mOverlay.isEmpty()) { mOverlay.getOverlayView().dispatchDraw(canvas); } // we're done... return; } // Step 2, save the canvas' layers ... // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 5, draw the fade effect and restore layers // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); }

注:

  • View有一個(gè)特殊的方法setWillNotDraw。假設(shè)一個(gè)View不須要繪制不論什么內(nèi)容,設(shè)置這個(gè)標(biāo)記位true后,系統(tǒng)會(huì)進(jìn)行優(yōu)化。默認(rèn)情況下,View沒有啟用這個(gè)優(yōu)化標(biāo)記位,可是ViewGroup會(huì)默認(rèn)啟用這個(gè)優(yōu)化標(biāo)記位。

  • 這個(gè)標(biāo)記位對實(shí)際開發(fā)的意義是:假設(shè)自己定義控件繼承于ViewGroup而且本身不具備繪制功能時(shí),就能夠開啟這個(gè)標(biāo)記位從而便于系統(tǒng)進(jìn)行興許的優(yōu)化。當(dāng)明白知道一個(gè)ViewGroup須要通過onDraw來繪制內(nèi)容時(shí),須要顯示地關(guān)閉WILL_NOT_DRAW這個(gè)標(biāo)記位。

歡迎轉(zhuǎn)載。轉(zhuǎn)載請注明本文鏈接http://blog.csdn.net/l664675249/article/details/50774617

總結(jié)

以上是生活随笔為你收集整理的ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。