亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

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

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 自己發(fā)揮。 EXACTLY 精確模式,這種模式下使用后邊的 specSize ,一般對應于 LayoutParams 的 match_content 和設置的精確尺寸。 AT_MOST 最大模式,這種模式下 view 的最大尺寸不能超過后邊的 specSize ,一般對應于 LayoutParams 的 wrap_content

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

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

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

protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {//拿到子元素的 LayoutParams 參數 final LayoutParams lp = child.getLayoutParams(); //創(chuàng)建子元素的 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; // 父容器沒有限制,子元素自己發(fā)揮 case MeasureSpec.UNSPECIFIED:if (childDimension >= 0) { //子元素自己有設置的值,就好實用自己的值,設置為精確模式 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) { // 子元素想充滿父容器,那就找到父容器的尺寸,但父容器的尺寸未知,還是要自己發(fā)揮 UNSPECIFIED。 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) { // 只元素是包裹自身,父容器無法給出參考,所以讓子元素自己去隨意發(fā)揮,仍然是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 類型的方法,里面的實現比較簡單會有一些判斷調整,是否需要測量,會繼續(xù)調用 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 的影響下創(chuàng)建出子元素的 measureSpec ,然后將調用子元素的 measure() 將measureSpec 傳遞進去。

這里就將測量傳遞到了子元素。如果子元素是單純的 View 控件只需要完成自己就可以了,如果是 ViewGroup 會繼續(xù)將測量遞歸下去,直至完成整個 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() 設置自己四個頂點的位置。

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

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

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

在這里也是將布局流程傳遞到了子元素,如果子元素是 ViewGroup 會繼續(xù)將布局流程傳遞,直到完成整個 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 樹的根節(jié)點,所有的繪制,事件都是從這個 View 開始分發(fā)的。

它繼承自 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 的繪制,觸屏,按鍵,屏幕刷新等事件分發(fā)都通過它完成的。

Activity 視圖結構

Android view繪制流程詳解

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

標簽: Android
相關文章:
主站蜘蛛池模板: 伊人色综合久久天天爱 | 国产婷婷一区二区三区 | 草草影院ccyy国产日本欧美 | 精品欧美成人bd高清在线观看 | 欧美日韩亚洲精品一区二区三区 | 在线三级播放 | 精品免费久久 | 日韩视频一区 | 国产高清视频在线播放 | 欧美日韩亚洲一区二区 | 免费看的黄色 | 国产精品网站 夜色 | 欧美一级aⅴ毛片 | 久久这里精品青草免费 | 色婷婷六月丁香在线观看 | 亚洲精品乱码国产精品乱码 | 美女黄18岁以下禁看 | 久久精品亚洲 | 国产精品1页 | 亚洲乱理伦片在线看中字 | 97射射| 免费观看黄色在线视频 | 免费国产一级特黄久久 | 久久久久欧美精品 | 麻豆视频官网 | 亚洲精品欧美日本中文字幕 | 国产精品五月色六月婷婷 | 中国一级特黄真人毛片免 | 国产精品欧美一区喷水 | 人人九九| 中文字幕久久久久一区 | 国产v视频 | 国产久视频观看 | 亚洲综合精品一二三区在线 | 夜夜爽影院 | 免费观看黄色视屏 | 国产成人久久精品二区三区牛 | 色视频在线观看网站 | 日韩在线看片中文字幕不卡 | 中文在线观看视频 | 亚欧黄色 |