最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android UI繪制流程及原理詳解

 更新時(shí)間:2019年08月04日 14:36:45   作者:JoahYau  
在本篇文章里小編給大家整理的是關(guān)于Android UI繪制流程及原理以及相關(guān)知識(shí)點(diǎn),需要的朋友們可以學(xué)習(xí)下。

一、繪制流程源碼路徑

1、Activity加載ViewRootImpl

ActivityThread.handleResumeActivity() 
--> WindowManagerImpl.addView(decorView, layoutParams) 
--> WindowManagerGlobal.addView()

2、ViewRootImpl啟動(dòng)View樹(shù)的遍歷

ViewRootImpl.setView(decorView, layoutParams, parentView)
-->ViewRootImpl.requestLayout()
-->scheduleTraversals()
-->TraversalRunnable.run()
-->doTraversal()
-->performTraversals()(performMeasure、performLayout、performDraw)

二、View繪制流程

1、measure

(1)MeasureSpec是什么?

重寫(xiě)過(guò)onMeasure()方法都知道,測(cè)量需要用到MeasureSpec類獲取View的測(cè)量模式和大小,那么這個(gè)類是怎樣存儲(chǔ)這兩個(gè)信息呢?

留心觀察的話會(huì)發(fā)現(xiàn),onMeasure方法的兩個(gè)參數(shù)實(shí)際是32位int類型數(shù)據(jù),即:

00 000000 00000000 00000000 00000000

而其結(jié)構(gòu)為 mode + size ,前2位為mode,而后30位為size。

==> getMode()方法(measureSpec --> mode):

private static final int MODE_SHIFT = 30;
// 0x3轉(zhuǎn)換為二進(jìn)制即為:11
// 左移30位后:11000000 00000000 00000000 00000000
private static final int MODE_MASK = 0x3 << MODE_SHIFT;

public static int getMode(int measureSpec) {
 // 與MODE_MASK按位與運(yùn)算后,即將低30位清零,結(jié)果為mode左移30位后的值
 return (measureSpec & MODE_MASK);
}

getSize()方法同理。

==> makeMeasureSpec()方法(mode + size --> measureSpec):

public static int makeMeasureSpec(
 @IntRange(from = 0, 
  to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size, 
 @MeasureSpecMode int mode) {
 if (sUseBrokenMakeMeasureSpec) {
  return size + mode;
 } else {
  return (size & ~MODE_MASK) | (mode & MODE_MASK);
 }
}

這里解釋一下,按位或左側(cè)為size的高2位清零后的結(jié)果,右側(cè)為mode的低30位清零后的結(jié)果,兩者按位或運(yùn)算的結(jié)果正好為高2位mode、低30位size,例:

01000000 00000000 00000000 00000000 | 
00001000 00001011 11110101 10101101 =
01001000 00001011 11110101 10101101

二進(jìn)制計(jì)算規(guī)則可參考:http://www.fzitv.net/article/166892.htm

==> 測(cè)量模式:

public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY  = 1 << MODE_SHIFT;
public static final int AT_MOST  = 2 << MODE_SHIFT;

UNSPECIFIED:父容器不對(duì)View作任何限制,系統(tǒng)內(nèi)部使用。

EXACTLY:精確模式,父容器檢測(cè)出View大小,即為SpecSize;對(duì)應(yīng)LayoutParams中的match_parent和指定大小的情況。

AT_MOST:最大模式,父容器指定可用大小,View的大小不能超出這個(gè)值;對(duì)應(yīng)wrap_content。

(2)ViewGroup的測(cè)量流程

回到ViewRootImpl的performMeasure方法,這里傳入的參數(shù)為頂層DecorView的測(cè)量規(guī)格,其測(cè)量方式為:

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
 int measureSpec;
 switch (rootDimension) {

 case ViewGroup.LayoutParams.MATCH_PARENT:
  measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
  break;
 case ViewGroup.LayoutParams.WRAP_CONTENT:
  measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
  break;
 default:
  measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
  break;
 }
 return measureSpec;
}

match_parent和具體數(shù)值大小為EXACTLY模式,wrap_content則為AT_MOST模式。

往下走,performMeasure方法中調(diào)用了DecorView的onMeasure方法,而DecorView繼承自FrameLayout,可以看到FL的onMeasure方法中調(diào)用了measureChildWithMargins方法,并傳入自身的測(cè)量規(guī)格:

protected void measureChildWithMargins(View child,
  int parentWidthMeasureSpec, int widthUsed,
  int parentHeightMeasureSpec, int heightUsed) {
 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
   mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
     + widthUsed, lp.width);
 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
   mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
     + heightUsed, lp.height);

 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

即測(cè)量子控件的大小,測(cè)量規(guī)則詳情可看getChildMeasureSpec方法,總結(jié)如下:

childLayoutParams\parentSpecMode EXACTLY AT_MOST UNSPECIFIED
dp EXACTLY/childSize EXACTLY/childSize EXCATLY/childSize
match_parent EXACTLY/parentSize AT_MOST/parentSize UNSPECIFIED/0
wrap_content AT_MOST/parentSize AT_MOST/parentSize UNSPECIFIED/0

回到onMeasure方法,測(cè)完子控件之后,ViewGroup會(huì)經(jīng)過(guò)一些計(jì)算,得出自身大?。?/p>

// 加上padding
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

// 檢查是否小于最小寬度、最小高度
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

// 檢查Drawable的最小高度和寬度
final Drawable drawable = getForeground();
if (drawable != null) {
 maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
 maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}

setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
  resolveSizeAndState(maxHeight, heightMeasureSpec,
    childState << MEASURED_HEIGHT_STATE_SHIFT));

綜上,ViewGroup的測(cè)量需要先測(cè)量子View的大小,而后結(jié)合padding等屬性計(jì)算得出自身大小。

(3)View的測(cè)量流程

View.performMeasure()
-->onMeasure(int widthMeasureSpec, int heightMeasureSpec)
-->setMeasuredDimension(int measuredWidth, int measuredHeight)
-->setMeasuredDimensionRaw(int measuredWidth, int measuredHeight)

可以看到setMeasuredDimensionRaw()方法:

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
 // 存儲(chǔ)測(cè)量結(jié)果
 mMeasuredWidth = measuredWidth;
 mMeasuredHeight = measuredHeight;

 // 設(shè)置測(cè)量完成的標(biāo)志位
 mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}

View不需要考慮子View的大小,根據(jù)內(nèi)容測(cè)量得出自身大小即可。

另外,View中的onMeasure方法中調(diào)用到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:
  // 最終測(cè)量的結(jié)果都是父容器的大小
  result = specSize;
  break;
 }
 return result;
}

這里看到精確模式和最大模式,最終測(cè)量的結(jié)果都是父容器的大小,即布局中的wrap_content、match_parent以及數(shù)值大小效果都一樣,這也就是自定義View一定要重寫(xiě)onMeasure方法的原因。

2、layout

布局相對(duì)測(cè)量而言要簡(jiǎn)單許多,從ViewRootImpl的performLayout方法出發(fā),可以看到其中調(diào)用了DecorView的layout方法:

// 實(shí)則為DecorView的left, top, right, bottom四個(gè)信息
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

進(jìn)入layout方法,發(fā)現(xiàn)l、t、r、b被傳遞到了setFrame方法中,并設(shè)置給了成員變量:

mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;

所以,布局實(shí)際為調(diào)用View的layout方法,設(shè)置自身的l、t、r、b值。另外,layout方法中往下走,可以看到調(diào)用了onLayout方法,進(jìn)入后發(fā)現(xiàn)為空方法。因而查看FrameLayout的onLayout方法:

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
 layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}

void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
 final int count = getChildCount();

 // 省略

 for (int i = 0; i < count; i++) {
  final View child = getChildAt(i);
  if (child.getVisibility() != GONE) {
   final LayoutParams lp = (LayoutParams) child.getLayoutParams();

   // 省略

   child.layout(childLeft, childTop, childLeft + width, childTop + height);
  }
 }
}

可以看到,進(jìn)行一系列計(jì)算后,調(diào)用了child的layout方法,對(duì)子控件進(jìn)行布局,同時(shí)子控件又會(huì)繼續(xù)往下對(duì)自己的子控件布局,從而實(shí)現(xiàn)遍歷。

綜上,布局實(shí)際為調(diào)用layout方法設(shè)置View位置,ViewGroup則需要另外實(shí)現(xiàn)onLayout方法擺放子控件。

3、draw

(1)繪制過(guò)程入口

ViewRootImpl.performDraw()
-->ViewRootImpl.draw()
-->ViewRootImpl.drawSoftware()
-->View.draw()

(2)繪制步驟

進(jìn)入到View的draw方法中,可以看到以下一段注釋:

/*
 * Draw traversal performs several drawing steps which must be executed
 * in the appropriate order:
 *
 *  1. Draw the background
 *  2. If necessary, save the canvas' layers to prepare for fading
 *  3. Draw view's content
 *  4. Draw children
 *  5. If necessary, draw the fading edges and restore layers
 *  6. Draw decorations (scrollbars for instance)
 */

結(jié)合draw方法的源碼,繪制過(guò)程的關(guān)鍵步驟如下:

  1. ==> 繪制背景:drawBackground(canvas)
  2. ==> 繪制自己:onDraw(canvas)
  3. ==> 繪制子view:dispatchDraw(canvas)
  4. ==> 繪制滾動(dòng)條、前景等裝飾:onDrawForeground(canvas)

感謝大家的閱讀和對(duì)腳本之家的支持。

相關(guān)文章

  • Android跨進(jìn)程拋異常的原理的實(shí)現(xiàn)

    Android跨進(jìn)程拋異常的原理的實(shí)現(xiàn)

    這篇文章主要介紹了Android跨進(jìn)程拋異常的原理的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-11-11
  • Android實(shí)現(xiàn)連連看游戲

    Android實(shí)現(xiàn)連連看游戲

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)連連看游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Assert.assertEquals()方法參數(shù)詳解

    Assert.assertEquals()方法參數(shù)詳解

    本文詳細(xì)講解了Assert.assertEquals()方法參數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • kotlin淺析when與循環(huán)的使用

    kotlin淺析when與循環(huán)的使用

    這篇文章主要介紹了kotlin語(yǔ)言中when與循環(huán)的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 在Android項(xiàng)目中使用AspectJ的方法

    在Android項(xiàng)目中使用AspectJ的方法

    這篇文章主要介紹了在Android項(xiàng)目中使用AspectJ的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • android ListView的右邊滾動(dòng)滑塊啟用方法 分享

    android ListView的右邊滾動(dòng)滑塊啟用方法 分享

    android ListView的右邊滾動(dòng)滑塊啟用方法 分享,需要的朋友可以參考一下
    2013-05-05
  • Android實(shí)現(xiàn)Activity界面切換添加動(dòng)畫(huà)特效的方法

    Android實(shí)現(xiàn)Activity界面切換添加動(dòng)畫(huà)特效的方法

    這篇文章主要介紹了Android實(shí)現(xiàn)Activity界面切換添加動(dòng)畫(huà)特效的方法,非常實(shí)用的技巧,需要的朋友可以參考下
    2014-08-08
  • Android?TextView冷門實(shí)用方法技巧

    Android?TextView冷門實(shí)用方法技巧

    這篇文章主要為大家介紹了Android?TextView冷門實(shí)用方法技巧示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • 基于adbkit的android設(shè)備管理(精簡(jiǎn)版stf)

    基于adbkit的android設(shè)備管理(精簡(jiǎn)版stf)

    這篇文章主要為大家介紹了基于adbkit的android設(shè)備管理(精簡(jiǎn)版stf)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Android錄音并且輸出為Mp4文件的方法教程

    Android錄音并且輸出為Mp4文件的方法教程

    這篇文章主要給大家介紹了關(guān)于Android錄音并且輸出為Mp4文件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08

最新評(píng)論

铜山县| 讷河市| 凤庆县| 新巴尔虎左旗| 饶河县| 科技| 宁夏| 大邑县| 监利县| 荥经县| 报价| 会宁县| 曲阜市| 邵阳市| 肇庆市| 淅川县| 石家庄市| 石景山区| 淳化县| 磐安县| 乐亭县| 临夏县| 仁怀市| 武穴市| 黔江区| 峡江县| 绍兴市| 郯城县| 东城区| 凤凰县| 萨嘎县| 营口市| 乌海市| 乌拉特中旗| 成安县| 河南省| 永胜县| 项城市| 克什克腾旗| 手机| 祥云县|