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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

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

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

原文鏈接 http://sparkyuan.me/ 轉載請注明出處

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

ViewRoot和DecorView

ViewRoot

  • ViewRoot相應ViewRootImpl類,它是連接WindowManager和DecorView的紐帶,View的三大流程均通過ViewRoot來完畢。
  • ActivityThread中。Activity創建完畢后,會將DecorView加入到Window中,同一時候創建ViewRootImpl對象,并建立兩者的關聯。

DecorView

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

MeasureSpec

在測量過程中,系統會將View的LayoutParams依據父容器所施加的規則轉換成相應的MeasureSpec。然后再依據這個MeasureSpec來測量出View的寬和高。測量出來的寬和高不一定等于View終于的寬和高。


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

SpecMode有三類:

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

普通MeasureSpec的創建規則

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

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

View的工作流程

measure

ViewGroup的measure方法會遍歷每一個子元素,并調用子元素內部的measure方法,measure源代碼例如以下:

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

注:

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

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

怎樣得到View的寬和高

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

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

  • Activity或者View的onWindowFocusChanged方法(注意該方法會在Activity Pause和resume時被多次調用)
  • view.post(new Runnable( {@Overidde public void run(){})}),在run方法中獲取。
  • ViewTreeObserver中的onGlobalLayoutListener中。
  • 手動調用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()確定四個頂點的位置,進而確定View在父容器中的位置。

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

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有一個特殊的方法setWillNotDraw。假設一個View不須要繪制不論什么內容,設置這個標記位true后,系統會進行優化。默認情況下,View沒有啟用這個優化標記位,可是ViewGroup會默認啟用這個優化標記位。

  • 這個標記位對實際開發的意義是:假設自己定義控件繼承于ViewGroup而且本身不具備繪制功能時,就能夠開啟這個標記位從而便于系統進行興許的優化。當明白知道一個ViewGroup須要通過onDraw來繪制內容時,須要顯示地關閉WILL_NOT_DRAW這個標記位。

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

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。