国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術文章
文章詳情頁

Android view繪制流程詳解

瀏覽:4日期:2022-09-18 14:16:51
目錄繪制流程Measure 測量流程MeasureSpeclayout 布局流程draw 繪制流程相關類 & 概念DecorViewWindowViewRootActivity 視圖結構繪制流程 measure 流程測量出 View 的寬高尺寸。 layout 流程確定 View 的位置及最終尺寸。 draw 流程將 View 繪制在屏幕上。Measure 測量流程

系統是通過 MeasureSpec 測量 View 的,在了解測量過程之前一定要了解這個 MeasureSpec 。

MeasureSpec

MeasureSpec 是一個 32 位的 int 值打包而來的,打包為 MeasureSpec 主要是為了避免過多的對象內存分配。

為了方便操作,MeasureSpec 提供了快捷的打包和解包的快捷方法。

MeasureSpec.makeMeasureSpec( int size, int mode) MeasureSpec.getMode(int measureSpec) MeasureSpec.getSize(int measureSpec)

MeasureSpec 其中前 2 位表示測量的模式 SpecMode,后邊 30 位表示某種測量模式下的尺寸 SpecSize。

MeasureSpec 中有三種測量模式

UNSPECIFIED 不指定具體尺寸,完全由 View 自己發揮。 EXACTLY 精確模式,這種模式下使用后邊的 specSize ,一般對應于 LayoutParams 的 match_content 和設置的精確尺寸。 AT_MOST 最大模式,這種模式下 view 的最大尺寸不能超過后邊的 specSize ,一般對應于 LayoutParams 的 wrap_content

在測量 View 的時候,系統會將自己的 LayoutParams 參數在父容器的 MeasureSpec 影響下轉換為自己的MeasureSpec ,然后再通過這個 MeasureSpec 測量自身的寬高。

需要注意的是View 的MeasureSpec 不是唯一由 LayoutParams 決定的,是在父容器的共同影響下創建來的。

在 ViewGroup 的 measureChild() 可以看到具體的實現思路,getChildMeasureSpec() 里就是將 layoutParams 轉換為 measureSpec 的實現思路。

protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {//拿到子元素的 LayoutParams 參數 final LayoutParams lp = child.getLayoutParams(); //創建子元素的 measureSpec final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); //將測量傳遞到子元素 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}public static int getChildMeasureSpec(int spec, int padding, int childDimension) { //解析父容器的 measureSpec ,解析出模式和尺寸 int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // 父容器是精確模式的情況,設置了精確尺寸。 case MeasureSpec.EXACTLY:if (childDimension >= 0) {//子元素本身是設置的精確尺寸,就是EXACTLY 模式,尺寸就是設置的尺寸。 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) { // 子元素設置的 match_content 充滿入容器,就把尺寸設置為入容器的尺寸,模式設置為EXACTLY resultSize = size; resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) { // 包裹模式下,子元素可以自己設置尺寸,但是不能超過夫容器的尺寸。模式為AT_MOST,尺寸為父容器的尺寸。 resultSize = size; resultMode = MeasureSpec.AT_MOST;}break; //父容器是最大模式 case MeasureSpec.AT_MOST:if (childDimension >= 0) { // 設置為子元素的尺寸,為精確模式 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) { // 子元素想充滿父容器,應該設置為父容器的尺寸,但是父容器是最大模式,沒有精確尺寸。 // 所以將子元素設置為最大模式,不能超過父容器目前的尺寸。 resultSize = size; resultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) { // 子元素沒有精確尺寸,想包裹自身,這種模式下,設置為最大模式,不超過父容器尺寸就好。 // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST;}break; // 父容器沒有限制,子元素自己發揮 case MeasureSpec.UNSPECIFIED:if (childDimension >= 0) { //子元素自己有設置的值,就好實用自己的值,設置為精確模式 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) { // 子元素想充滿父容器,那就找到父容器的尺寸,但父容器的尺寸未知,還是要自己發揮 UNSPECIFIED。 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) { // 只元素是包裹自身,父容器無法給出參考,所以讓子元素自己去隨意發揮,仍然是UNSPECIFIED resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED;}break; } //使用打包方法,將子元素的模式和尺寸打包并返回 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}

measure 流程是在 ViewRoot 的 performMeasure() 里開始的。

在這里會將 DecorView 的 layoutParams 在 window 的 measureSpec 影響下轉換為自己的 measureSpec 。 然后調用 DecorView 的 measure() 將寬高的 measureSpec 傳入,在 measure() 里,decorView 開始自己的測量。

從 DecorView 的 measure() 開始,整個 View 樹的測量流程就開始了。

View 的測量都是在 measure() 里進行的,這是個 final 類型的方法,里面的實現比較簡單會有一些判斷調整,是否需要測量,會繼續調用 onMeasure() 將 measureSpec 傳進來,測量尺寸的確定最終是在 onMeasure() 里完成的。

通常我們自定義 View 都要重寫這個方法實現自己的測量邏輯,包括我們常用的控件都是自己重寫了這個方法實現自己的測量邏輯。

如果不重寫 onMeasure(),會導致自定義 view 的 wrap_content 參數無效,具體可以看一下 getDefaultSize() 實現。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED:result = size;break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY://默認 精確模式和最大模式下都是使用后邊的 specSize ,這會導致我們設置的 wrap_content 無效,始終是充滿父容器。result = specSize;break; } return result;}protected int getSuggestedMinimumHeight() { return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());} protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());}

View 和 ViewGroup 的測量過程是不同的。

單純的 View 只需要在 onMeasure() 里完成自己的測量就可以了,ViewGroup 除了完成自己的測量外,還有子元素的測量。

ViewGroup 的 onMeasure() 是沒有任何實現的,因為各個布局的特性不同,具體測量邏輯也是不同的,具體實現都在各個布局里。

但是 ViewGroup 里提供了 measureChildren() 方法,思路就是,遍歷所有需要顯示的子元素,取出他們的 LayoutParams 參數在自己 measureSpec 的影響下創建出子元素的 measureSpec ,然后將調用子元素的 measure() 將measureSpec 傳遞進去。

這里就將測量傳遞到了子元素。如果子元素是單純的 View 控件只需要完成自己就可以了,如果是 ViewGroup 會繼續將測量遞歸下去,直至完成整個 View 樹的測量。

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {final int size = mChildrenCount;final View[] children = mChildren;for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {//測量子元素,measureChild 見上面 MeasureSpec 里的代碼。measureChild(child, widthMeasureSpec, heightMeasureSpec); }} }

在完成測量流程之后就會進入了 layout 流程了。

layout 布局流程

layout 這一流程會確定 View 的四個頂點位置,進而確定在父容器中的位置和最終寬高。

layout 流程也是在 ViewRoot 里開始,是在 performLayout() 里首先調用 DecorView 的 layout() 方法開始整個 View 樹的布局流程。

View 的布局流程都是在 layout() 方法里完成的,會在這里通過 setFrame() 設置自己四個頂點的位置。

設置完自己的位置后,會繼續調用 onLayout() 方法,如果是 ViewGroup 可以繼續在 onLayout 里確定子元素的位置。

View 的 onLayout() 是沒有任何實現的,因為它是沒有子元素,ViewGroup 本身也是沒有實現的,也都是具體的各個布局里自己實現的。

思路也是遍歷所有需要布局的子元素,根據測量尺寸計算出他們的位置后調用子元素的 layout() 方法將位置參數穿進去,讓子元素去完成自己的布局流程。

在這里也是將布局流程傳遞到了子元素,如果子元素是 ViewGroup 會繼續將布局流程傳遞,直到完成整個 View 樹的布局流程。

layout() 確定自身的位置 onLayout() 確定子元素的位置

在完成 layout 流程后,就是最后一個 draw 流程了。

draw 繪制流程

這個流程是將 View 繪制到屏幕上。

draw 流程也是在 ViewRoot 里開始的,具體是在 performDraw() 里開始,在這里會調用 DecorView 的 draw() 開始整個 View 樹的繪制。

draw 的過程相對來說較為簡單,在 draw() 里可以看到整個步驟

繪制背景 drawBackground(canvas); 繪制自己的內容 onDraw(canvas); 繪制子元素 dispatchDraw(canvas); 繪制裝飾 onDrawForeground(canvas);

我們自定義 View 都會在 onDraw() 里實現自己的繪制邏輯,View 的 dispatchDraw() 是沒有任何實現的,具體實現在 ViewGroup 里。

在 ViewGroup 后調用子元素的 draw() 將繪制流程傳遞到子元素,直到繪制完整個 View 樹。

在完成整個 View 樹的繪制后,就可以在屏幕上看見界面了。

相關類 & 概念

在 View 的繪制過程中,涉及到了很多類,這里就不做詳細的介紹了,只在這里簡單列一下,知道這些個的作用。

DecorView

整個 View 樹的根節點,所有的繪制,事件都是從這個 View 開始分發的。

它繼承自 FrameLayout 是一個 ViewGroup ,內部含有一個 LinearLayout 。

這個 LinearLayout 里有一個 id 為 content 的 FrameLayout ,我們通常設置的 setContentView() 就是加載到了這個 FrameLayout 里。

Window

每個 Activity 都有一個 window ,直譯就是“窗口”,是 Activity 的成員變量,也是應用程序的視圖窗口,承載整個 Activity 的視圖。 內部含有一個 DeocrView 成員變量,承載的視圖就是這個 DeocrView 。

它目前只有一個實現類,PhoneWindow ,activity 里的 mWindow 就是這個實例。

ViewRoot

View Root 的作用很大,是連接 DecorView 和 Window Manager 的紐帶。 View 的繪制,觸屏,按鍵,屏幕刷新等事件分發都通過它完成的。

Activity 視圖結構

Android view繪制流程詳解

以上就是Android view繪制流程詳解的詳細內容,更多關于Android view繪制流程的資料請關注好吧啦網其它相關文章!

標簽: Android
相關文章:
主站蜘蛛池模板: 精品国产美女福利到在线不卡 | 日韩毛片高清在线看 | 久久久久亚洲精品影视 | 亚洲夜色| 九九夜色| 在线a亚洲视频播放在线观看 | 日韩毛片免费视频一级特黄 | 国产日韩欧美精品在线 | 伊人色在线观看 | 精品中文字幕不卡在线视频 | 看久久 | julia中文字幕久久亚洲 | 日本不卡免费高清视频 | 亚欧视频在线观看 | 黄色网址进入 | 国产精品免费看久久久香蕉 | 国产成a人亚洲精v品久久网 | 日本高清视频免费在线观看 | 毛片免费观看成人 | a亚洲天堂| 羞羞一区二区三区四区片 | 九九九九精品视频在线播放 | 免费看国产精品久久久久 | 国产亚洲在线 | 亚洲欧洲一区二区三区久久 | 午夜在线亚洲男人午在线 | 99精品国产综合久久久久 | 中国大陆一级毛片 | 欧美一级α片毛片免费观看 | 日韩影院久久 | 亚洲视频 在线观看 | 久久综合九色综合欧洲色 | xh98hx国产免费 | 全国最大色成免费网站 | 久久久久久一品道精品免费看 | 三级网址免费 | 不卡无毒免费毛片视频观看 | 视频亚洲一区 | 国产精品一级片 | 一个人看的日本免费视频 | 久久精品国产精品亚洲综合 |