中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android中View繪制流程的原理是什么

發布時間:2021-06-17 17:23:02 來源:億速云 閱讀:364 作者:Leah 欄目:大數據

Android中View繪制流程的原理是什么,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

setContentView流程

setContentView整個過程主要是如何把Activity的布局文件或者java的View添加至窗口里,重點概括為:

  1. 創建一個DecorView的對象mDecor,該mDecor對象將作為整個應用窗口的根視圖。

  2. 依據Feature等style theme創建不同的窗口修飾布局文件,并且通過findViewById獲取Activity布局文件該存放的地方(窗口修飾布局文件中id為content的FrameLayout)。

  3. 將Activity的布局文件添加至id為content的FrameLayout內。

  4. 當setContentView設置顯示OK以后會回調Activity的onContentChanged方法。Activity的各種View的findViewById()方法等都可以放到該方法中,系統會幫忙回調。

android的View繪制

view繪制主要包括三個方面:

  • measure 測量組件本身的大小

  • layout 確定組件在視圖中的位置

  • draw 根據位置和大小,將組件畫出來

視圖繪制的起點在ViewRootImpl類的performTraversals()方法,該方法完成的工作主要是: 根據之前的狀態,判定是否重新計算測試視圖大小(measure)、是佛重新放置視圖位置(layout)和是否重新重繪視圖(draw) ,部分源碼如下:

private void performTraversals() {
        ......        //最外層的根視圖的widthMeasureSpec和heightMeasureSpec由來
        //lp.width和lp.height在創建ViewGroup實例時等于MATCH_PARENT
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        ......
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        ......
        mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
        ......
        mView.draw(canvas);
        ......
    }

measure計算視圖大小

幾乎所有的組件都是繼承View類的,而關于view的測量工作,日常開發用得多的方法就是measure和onMeasure兩個方法,measure不可重寫,當我們自定義時主要重寫onMeasure方法即可,在方法內部我們必須完成組件的mMeasuredWidth和mMeasuredHeight實際尺寸測量,而這個尺寸是需要父視圖和子視圖共同決定的

measure流程從根視圖measure遍歷整個view樹結構,如下:

Android中View繪制流程的原理是什么

這里寫圖片描述

還要注意視圖尺寸MeasureSpec是一個組合尺寸,它是一個32位bit值,高兩位是尺寸模式specMode,低30位是尺寸大小值,我們可以利用提供的原聲庫方法很方便的進行尺寸組合和拆解:
specMode有三種: MeasureSpec.EXACTLY表示確定大小, MeasureSpec.AT_MOST表示最大大小, MeasureSpec.UNSPECIFIED不確定

int measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);    //合成int specMode = MeasureSpec.getMode(measureSpec);                                   //拆解int specSize = MeasureSpec.getSize(measureSpec);

而在視圖測量meause中,父組件傳給子組件的一般都是一個組合尺寸,我們可以拿出具體尺寸然后根據其他條件產生一個新的尺寸值,將這個值用setMeasuredDimension設置mMeasuredWidth和mMeasuredHeight具體尺寸,完成測量;

measure原理總結
  • MeasureSpec(View的內部類)測量規格為int型,值由高2位規格模式specMode和低30位具體尺寸specSize組成。其中specMode只有三種值:

    MeasureSpec.EXACTLY //確定模式,父View希望子View的大小是確定的,由specSize決定;MeasureSpec.AT_MOST //最多模式,父View希望子View的大小最多是specSize指定的值;MeasureSpec.UNSPECIFIED //未指定模式,父View完全依據子View的設計值來決定;
  • View的measure方法是final的,不允許重載,View子類只能重載onMeasure來完成自己的測量邏輯。

  • 最頂層DecorView測量時的MeasureSpec是由ViewRootImpl中getRootMeasureSpec方法確定的(LayoutParams寬高參數均為MATCH_PARENT,specMode是EXACTLY,specSize為物理屏幕大小)。

  • ViewGroup類提供了measureChild,measureChild和measureChildWithMargins方法,簡化了父子View的尺寸計算。

  • 只要是ViewGroup的子類就必須要求LayoutParams繼承子MarginLayoutParams,否則無法使用layout_margin參數。

  • View的布局大小由父View和子View共同決定。

  • 使用View的getMeasuredWidth()和getMeasuredHeight()方法來獲取View測量的寬高,必須保證這兩個方法在onMeasure流程之后被調用才能返回有效值。

layout視圖位置確定

layout的流程主要也是遍歷整個view樹結構,調用view.layout(int l, int t, int r, int b)確定好view的具體坐標位置,流程圖如下

Android中View繪制流程的原理是什么

這里寫圖片描述

當我們自定義一個組件時,通常時重寫onLayout方法,里面實現好自己的邏輯,最后在調用layout方法完成視圖位置確定,如果自定義組件時一個ViewGroup的話,還需要我們去遍歷每一個child確定尺寸

layout原理總結
  • 整個layout過程比較容易理解,從上面分析可以看出layout也是從頂層父View向子View的遞歸調用view.layout方法的過程,即父View根據上一步measure子View所得到的布局大小和布局參數,將子View放在合適的位置上。具體layout核心主要有以下幾點:

  • View.layout方法可被重載,ViewGroup.layout為final的不可重載,ViewGroup.onLayout為abstract的,子類必須重載實現自己的位置邏輯。

  • measure操作完成后得到的是對每個View經測量過的measuredWidth和measuredHeight,layout操作完成之后得到的是對每個View進行位置分配后的mLeft、mTop、mRight、mBottom,這些值都是相對于父View來說的。

  • 凡是layout_XXX的布局屬性基本都針對的是包含子View的ViewGroup的,當對一個沒有父容器的View設置相關layout_XXX屬性是沒有任何意義的(前面《Android應用setContentView與LayoutInflater加載解析機制源碼分析》也有提到過)。

  • 使用View的getWidth()和getHeight()方法來獲取View測量的寬高,必須保證這兩個方法在onLayout流程之后被調用才能返回有效值。

draw繪制

完成measure和Layout后,ViewRootImpl中的代碼會創建一個Canvas對象,然后調用View的draw()方法來執行具體的繪制工。所以又回歸到了ViewGroup與View的樹狀遞歸draw過程
先來看下View樹的遞歸draw流程圖,如下:

Android中View繪制流程的原理是什么

這里寫圖片描述

draw原理總結

可以看見,繪制過程就是把View對象繪制到屏幕上,整個draw過程需要注意如下細節:

  • 如果該View是一個ViewGroup,則需要遞歸繪制其所包含的所有子View。

  • View默認不會繪制任何內容,真正的繪制都需要自己在子類中實現。

  • View的繪制是借助onDraw方法傳入的Canvas類來進行的。

  • 區分View動畫和ViewGroup布局動畫,前者指的是View自身的動畫,可以通過setAnimation添加,后者是專門針對ViewGroup顯示內部子視圖時設置的動畫,可以在xml布局文件中對ViewGroup設置layoutAnimation屬性(譬如對LinearLayout設置子View在顯示時出現逐行、隨機、下等顯示等不同動畫效果)。

  • 在獲取畫布剪切區(每個View的draw中傳入的Canvas)時會自動處理掉padding,子View獲取Canvas不用關注這些邏輯,只用關心如何繪制即可。

  • 默認情況下子View的ViewGroup.drawChild繪制順序和子View被添加的順序一致,但是你也可以重載ViewGroup.getChildDrawingOrder()方法提供不同順序。

view提供的API控制視圖的方法

invalidate和postInvalidate方法源碼分析

請求重新繪制視圖,調用draw

  • invalidate在主線程調用

  • postInvalidate是在非主線程調用

View的requestLayout方法

requestLayout()方法會調用measure過程和layout過程,不會調用draw過程,也不會重新繪制任何View包括該調用者本身。

看完上述內容,你們掌握Android中View繪制流程的原理是什么的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

古田县| 呼和浩特市| 淮滨县| 巧家县| 万盛区| 高州市| 岫岩| 滨州市| 延川县| 贵溪市| 北辰区| 常宁市| 合肥市| 甘洛县| 临漳县| 遂宁市| 福贡县| 红安县| 吉水县| 武宣县| 山阳县| 前郭尔| 酒泉市| 腾冲县| 柳林县| 鄂温| 邓州市| 安塞县| 永兴县| 扎兰屯市| 交口县| 道孚县| 临安市| 新密市| 东方市| 团风县| 临海市| 日土县| 太保市| 潜江市| 宜宾县|