Android - View的绘制流程一(measure)

该博文所用的demo结构图:

相应的代码:

MainActivity.java:

[java] view
plain
 copy

  1. <span style="font-family:Microsoft YaHei;">public class MainActivity extends Activity {
  2. private int desiredWindowWidth;
  3. private int desiredWindowHeight;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. //获取屏幕的宽高,单位为dp
  9. desiredWindowWidth = Measurement.getScreenWidth(this);
  10. desiredWindowHeight = Measurement.getScreenHeight(this);
  11. Log.d("HWGT", "屏幕宽..=.." + desiredWindowWidth + "....屏幕高..=.." + desiredWindowHeight);
  12. }
  13. @Override
  14. protected void onPause() {
  15. super.onPause();
  16. //获取状态栏的高度(标题栏+content区域的top坐标)
  17. Rect frame = new Rect();
  18. getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
  19. int statusBarHeight = Measurement.px2dip(this, frame.top);
  20. //获取  标题栏+content 区域的高度
  21. int titleAndContentHeight = Measurement.px2dip(this, frame.height());
  22. //获取content区域的top坐标
  23. int tempContentTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
  24. int contentTop = Measurement.px2dip(this, tempContentTop);
  25. //标题栏的高度 = content区域的top坐标 - 状态栏的高度
  26. int titleBarHeight = contentTop - statusBarHeight;
  27. Log.d("HWGT", "titleBarHeight..=.."+titleBarHeight+
  28. "....contentTop..=.."+contentTop+"....statusBarHeight..=.." + statusBarHeight );
  29. }
  30. }</span>

activity_main.xml:

[java] view
plain
 copy

  1. <span style="font-family:Microsoft YaHei;"><com.hwgt.drawingprocessofview.ui.MyCustomLinearLayoutA
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:orientation="vertical"
  7. tools:context="com.hwgt.drawingprocessofview.MainActivity" >
  8. <com.hwgt.drawingprocessofview.ui.MyCustomTextViewA
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. android:layout_marginTop="13dp"
  12. android:layout_gravity="center_horizontal"
  13. android:text="@string/hello_world" />
  14. <com.hwgt.drawingprocessofview.ui.MyCustomLinearLayoutB
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. android:layout_marginTop="13dp">
  18. <com.hwgt.drawingprocessofview.ui.MyCustomButtonA
  19. android:layout_width="111dp"
  20. android:layout_height="wrap_content"
  21. android:layout_marginLeft="31dp"
  22. android:text="@string/ok"/>
  23. </com.hwgt.drawingprocessofview.ui.MyCustomLinearLayoutB>
  24. </com.hwgt.drawingprocessofview.ui.MyCustomLinearLayoutA></span>

Measurement.java:

[java] view
plain
 copy

  1. <span style="font-family:Microsoft YaHei;">public class Measurement {
  2. public static int px2dip(Context context, float pxValue) {
  3. final float scale = context.getResources().getDisplayMetrics().density;
  4. return (int) (pxValue / scale + 0.5f);
  5. }
  6. public static int getScreenWidth(Context context) {
  7. WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  8. DisplayMetrics dm = new DisplayMetrics();
  9. manager.getDefaultDisplay().getMetrics(dm);
  10. return px2dip(context, dm.widthPixels);
  11. }
  12. public static int getScreenHeight(Context context) {
  13. WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  14. DisplayMetrics dm = new DisplayMetrics();
  15. manager.getDefaultDisplay().getMetrics(dm);
  16. return px2dip(context, dm.heightPixels);
  17. }
  18. }</span>

MyCustomLinearLayoutA.java、MyCustomTextViewA.java、MyCustomLinearLayoutB.java 和 MyCustomButtonA.java类似,构造函数省略了,onMeasure()方法中的处理也一样:

[java] view
plain
 copy

  1. <span style="font-family:Microsoft YaHei;">public class MyCustomLinearLayoutA extends LinearLayout ... ...
  2. public class MyCustomTextViewA extends TextView ... ...
  3. public class MyCustomLinearLayoutB extends LinearLayout ... ...
  4. public class MyCustomButtonA extends Button {
  5. @Override
  6. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  7. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  8. int widthMeasureSpecSize = MeasureSpec.getSize(widthMeasureSpec);
  9. int heightMeasureSpecSize = MeasureSpec.getSize(heightMeasureSpec);
  10. Log.d("HWGT", "ButtonA: widthMeasureSpecSize..=.."+Measurement.px2dip(getContext(), widthMeasureSpecSize)
  11. + "....heightMeasureSpecSize..=.."+Measurement.px2dip(getContext(), heightMeasureSpecSize));
  12. }
  13. }</span>

Activity 的 setContentView() 方法运行后会调用到PhoneWindow
的 setContentView()方法

当第一次运行MainActivity 的 setContentView(R.layout.activity_main) 时,在PhoneWindow的setContentView(int layoutResID)方法中会进行下面操作:

1、调用installDecor()
--> generateDecor()方法创建一个DecorView(FrameLayout的子类)对象 mDecor

2、调用generateLayout(DecorView decor) 方法,
 该方法主要逻辑为:

A、依据requestFreature()和Activity节点的android:theme=""等值选择对应的窗体布局文件加入进第一步创建的mDecor对象中——

decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));(in即为对应的布局文件,而且,窗口布局是填充父窗口mDecor的)

Activity比較经常使用的窗体布局文件有R.layout.screen_title
和 R.layout.screen_simple

R.layout.screen_title :

[java] view
plain
 copy

  1. <span style="font-family:Microsoft YaHei;"><LinearLayout
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:fitsSystemWindows="true">
  5. <FrameLayout
  6. android:layout_width="match_parent"
  7. android:layout_height="?

    android:attr/windowTitleSize"

  8. style="?android:attr/windowTitleBackgroundStyle">
  9. <TextView
  10. android:id="@android:id/title"
  11. style="?android:attr/windowTitleStyle"
  12. android:background="@null"
  13. android:fadingEdge="horizontal"
  14. android:gravity="center_vertical"
  15. android:layout_width="match_parent"
  16. android:layout_height="match_parent" />
  17. </FrameLayout>
  18. <FrameLayout
  19. android:id="@android:id/content"
  20. android:layout_width="match_parent"
  21. android:layout_height="0dip"
  22. android:layout_weight="1"
  23. android:foregroundGravity="fill_horizontal|top"
  24. android:foreground="?android:attr/windowContentOverlay" />
  25. </LinearLayout></span>

R.layout.screen_simple:

[java] view
plain
 copy

  1. <span style="font-family:Microsoft YaHei;"><FrameLayout
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@android:id/content"
  4. android:fitsSystemWindows="true"
  5. android:foregroundInsidePadding="false"
  6. android:foregroundGravity="fill_horizontal|top"
  7. android:foreground="?android:attr/windowContentOverlay" /> </span>

这两个布局都包括一个id为content的FrameLayout布局,比方我们设置了requestWindowFeature(Window.FEATURE_NO_TITLE);或<item
name="android:windowNoTitle">true</item>时。R.layout.screen_simple将被加入到mDecor中。

B、返回 通过findViewById()找到的id为content的布局mContentParent

3、运行mLayoutInflater.inflate(layoutResID, mContentParent); 将我们的 R.layout.activity_main
布局加入到 mContentParent 中。

所以,mDecor对象就是应用程序窗体的根view,该demo的效果图和其涉及到的相关布局的关系例如以下图:

紫色框代表根view -- mDecor

蓝色框代表generateLayout(mDecor) 方法中向mDecor里加入的布局,图一中为R.layout.screen_simple,图二中为R.layout.screen_title

(状态栏是绘制在紫色框mDecor里还是蓝色框里还有待研究,这里如果状态栏的父窗口就是mDecor,不影响对view绘制的理解)

红色框代表R.layout.screen_simple 或 R.layout.screen_title 中 id为content的布局 -- mContentParent

绿色框代表R.layout.activity_main

白色框则是标题栏

那么,这些view对象都是怎么绘制到屏幕上的呢?

核心逻辑都在ViewRoot的performTraversals()方法中。主要分为三个阶段:

(该图截取自Android内核剖析一书,谢谢作者)

第一个阶段是measure。第二个阶段是layout,第三个阶段是draw

一、mesarue

performTraversals()方法中关于measure的代码(进行了简化,仅为了解大致流程)例如以下:

例如以下:

private void performTraversals() {

final View host = mView;    // mView就是一个DecorView对象,所以,view的绘制是从根view
- - DecorView開始的

int desiredWindowWidth;

int desiredWindowHeight;

int childWidthMeasureSpec;

int childHeightMeasureSpec;

//获取手机屏幕分辨率

DisplayMetrics packageMetrics =  mView.getContext().getResources().getDisplayMetrics();

desiredWindowWidth = packageMetrics.widthPixels;

desiredWindowHeight = packageMetrics.heightPixels;

1、childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);

childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight,
lp.height);

//传入的參数为手机屏幕的宽和高。 lp.width 和 lp.height
(一般为 MATCH_PARENT)

private int getRootMeasureSpec(int windowSize, int rootDimension) {

int measureSpec;

switch (rootDimension) {

case ViewGroup.LayoutParams.MATCH_PARENT:

// Window can‘t resize. Force root view to be windowSize.

measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);

public static int makeMeasureSpec(int size, int mode) {

return size + mode;

}

break;

case ViewGroup.LayoutParams.WRAP_CONTENT:

// Window can resize. Set max size for root view.

measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);

break;

default:

// Window wants to be an exact size. Force root view to be that size.

measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);

break;

}

return measureSpec;

}

2、host.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

上边就是从根view開始绘制整个view树的主要代码,比較重要的是第1和第2处

首先,第1处:

由屏幕的宽和高分别与MeasureSpec.EXACTLY进行组合得到childWidthMeasureSpec 和 childHeightMeasureSpec 的值,逻辑比較简单,主要是涉及到一个MeasureSpec类。它是view的一个静态内部类,看它的凝视:

一个measurespec封装了父视图对子视图在长度或宽度上的要求。一个measurespec由大小和模式组成(使用makeMeasureSpec方法获取)。

当中,模式有三个可选值:

UNSPECIFIED:代表父视图对子视图在长度或宽度上不施加不论什么约束。子视图能够是不论什么它想要的大小

EXACTLY:代表父视图已经对子视图在长度或宽度上确定了一个准确的尺寸,无论子视图想要多大。它都会受到这个尺寸的限制

AT_MOST:代表子视图最多仅仅能是设置的大小

在measurespec类中,主要有三个经常使用的方法:

public static int makeMeasureSpec(int size, int mode)

//该方法用于将一个详细的尺寸值和一个mode(比方EXACTLY)组合成一个measureSpec

public static int getMode(int measureSpec)

//该方法用于从一个measureSpec中取出对应的mode(比方EXACTLY)

public static int getSize(int measureSpec)

//该方法用于从一个measureSpec中取出详细的size

然后,第2处:

将第1处计算得到的childWidthMeasureSpec 和 childHeightMeasureSpec 作为參数,运行host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
(代码经简化,仅为理解大致流程)

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {

onMeasure(widthMeasureSpec, heightMeasureSpec);

}

measure()方法定义在view中,是final类型的,子类不能重写,它里边调用了onMeasure()方法,在这里,假设是非ViewGroup类型的view对象,则运行view类中的onMeasure方法。而对于ViewGroup类型的对象比方FrameLayout和LinearLayout来讲,由于ViewGroup类中并没有重写onMeasure()方法,所以都将运行自己类中的onMeasure()方法,它们的大致逻辑例如以下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int count = getChildCount();

for (int i = 0; i < count; i++) {

final View child = getChildAt(i);

//遍历,运行
measureChildWithMargins(child, ... ... ) 方法(定义在ViewGroup类中)

measureChildWithMargins(child, ... ...

protected void measureChildWithMargins(View child,

int parentWidthMeasureSpec, int widthUsed,

int parentHeightMeasureSpec, int heightUsed) {

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

}

}

终于在measureChildWithMargins方法中又调用了measure方法,所以view的绘制是一个从根view到子view递归的过程。

如上文的图一和图二所看到的,在该博文的demo中,首先运行根view -- mDecor的measure方法,在mDecor的父类FrameLayout的onMeasure方法里进行遍历,然后mDecor的子类
—— 向mDecor里加入的窗体布局(图一中为R.layout.screen_simple,图二中为R.layout.screen_title)的measure方法得到运行,再在R.layout.screen_simple或R.layout.screen_title的onMeasure方法中进行遍历,运行到id为content的布局相应的measure方法,...
... ,直到该博文demo中的MyCustomLinearLayoutA的measure和onMeasure方法。

因为从DecorView到MyCustomLinearLayoutA进行遍历的过程中,还涉及到状态栏绘制的问题。本篇博文就不具体写了,但须要关注一点,就是onMeasure方法接收的參数的问题,这也是我们自己定义view重写onMeasure方法时遇到的问题之中的一个。

从performTraversals()方法中的host.measure(childWidthMeasureSpec, childHeightMeasureSpec)開始,在遍历的整个过程中,measure和onMeasure方法接收的參数都是一个measurespec,它封装了父视图对子视图在长度或宽度上的要求。我们能够通过MeasureSpec的getSize和getMode方法解析出相应的size和mode。

performTraversals()方法中host.measure方法接收的參数相应的size —— MeasureSpec.getSize(childWidthMeasureSpec)和MeasureSpec.getSize(childHeightMeasureSpec)正好是屏幕的宽和高

执行上述demo,打印log例如以下(測试手机1920*1080。单位所有换算为dp):

在设置全屏和非全屏的情况下,MyCustomLinearLayoutA的onMeasure方法接收的參数相应的size —— MeasureSpec.getSize(widthMeasureSpec)和MeasureSpec.getSize(heightMeasureSpec)正好各自是上文图一和图二中红色部分即id为content的布局的宽和高(參考MainActivity的onPause方法)。

那是不是每个view的onMeasure方法接收的參数相应的size都是其父视图的宽高呢?带着这个问题,我们就以MyCustomLinearLayoutA为例,从onMeasure()方法開始分析一个ViewGroup类型的视图遍历—measure子视图的具体过程。

LinearLayout类中的onMeasure()方法:

[java] view
plain
 copy

  1. <span style="font-family:Microsoft YaHei;">protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  2. if (mOrientation == VERTICAL) {
  3. measureVertical(widthMeasureSpec, heightMeasureSpec);
  4. } else {
  5. measureHorizontal(widthMeasureSpec, heightMeasureSpec);
  6. }
  7. }</span>

依据LinearLayout的布局方向调用measureVertical或measureHorizontal,以下以本文demo中MyCustomLinearLayoutA为例分析竖直方向的measureVertical。

该方法主要分为两个步骤(參考Android内核剖析第13章 — view工作原理):

一、进行遍历,跳过那些 lp.weight > 0 的子视图,调用measureChildBeforeLayout ----> measureChildWithMargins  方法对子视图进行measure,之后调用child.getMeasuredHeight()获取子视图的终于高度并加入到mTotalLength中。

二、把父视图剩余的高度依照weight大小均匀分配给子视图

本文仅仅为分析measure的流程,所以临时将weight>0的视图的測量过程省略掉了

void measureVertical(int widthMeasureSpec, int heightMeasureSpec)
{

//该变量用来存储全部子view的高度

mTotalLength = 0;

//获取子view的个数

final int count = getVirtualChildCount();

//从父view传递给MyCustomLinearLayoutA的MeasureSpec中获取宽和高的mode,这里都为EXACTLY

final int widthMode = MeasureSpec.getMode(widthMeasureSpec);

final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

for (int i = 0; i < count; ++i) {

// totalWeight
== 0 ? mTotalLength : 0 。这里跳过了weight>0的子视图。所以值为mTotalLength(在对MyCustomTextViewA进行measure时。值为0。另外,測量的是竖直方向。所以第4个參数直接传入0)

measureChildBeforeLayout(child,
i, widthMeasureSpec, 0, heightMeasureSpec,totalWeight == 0 ? mTotalLength : 0);

void measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth, int
heightMeasureSpec,int totalHeight) {

measureChildWithMargins(child, widthMeasureSpec, totalWidth, heightMeasureSpec, totalHeight);

//(在对MyCustomTextViewA进行measure时,parentWidthMeasureSpec 和 parentHeightMeasureSpec 为父view传递给

//  MyCustomLinearLayoutA的WidthMeasureSpec 和 HeightMeasureSpec。widthUsed 和 heightUsed 为MyCustomTextViewA

//  已经使用的水平和竖直方向的尺寸,这里都为0)

protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
int widthUsed,

int parentHeightMeasureSpec, int heightUsed) {

// 首先,拿到 child
即 MyCustomTextViewA 的布局參数

final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

//  然后,计算 childWidthMeasureSpec 和 childHeightMeasureSpec 的值

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);

//  第一个參数:是MyCustomLinearLayoutA的父view
— id为content的布局传递来的WidthMeasureSpec

//  我们能够通过 MeasureSpec.getSize
和 MeasureSpec.getMode 得到对应的size和mode(360dp MeasureSpec.EXACTLY)

//  第二个參数:将 父视图的padding值、child的margin值、父视图中已使用的尺寸
 这三项进行相加

//  我们能够把它理解为 除 MyCustomTextViewA
的本身尺寸之外 全部空间的总和

//
 第三个參数:child的width值(childHeightMeasureSpec 同理)

//  共同计算出childWidthMeasureSpec 的值

//  所以,getChildMeasureSpec 这种方法的作用。就是依据

//  1、父视图 MyCustomLinearLayoutA 的 MeasureSpec

//  2、 除 MyCustomTextViewA
的本身尺寸之外 全部空间的总和

//  3、MyCustomTextViewA 本身的尺寸

//  这三个方面共同来计算出 MyCustomTextViewA 
的 MeasureSpec

//  来看详细的逻辑:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

//  得到父视图的mode和size

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) {

// Parent has imposed an exact size on us

//  因为本demo中。MyCustomTextViewA  父视图的 specMode  为 EXACTLY。所以,仅仅看一条case语句了:

case MeasureSpec.EXACTLY:

//  在case语句中推断完父视图MyCustomLinearLayoutA 的mode之后,再来推断MyCustomTextViewA 的尺寸

//  也就是上文通过布局參数拿到的 我们在布局文件中写的宽和高的值

if (childDimension >= 0) {

//  假设我们在布局文件里为 MyCustomTextViewA  设置了详细的宽和高的值。那么将会用这个详细的值和EXACTLY

//  组合成一个MeasureSpec并返回

resultSize = childDimension;

resultMode = MeasureSpec.EXACTLY;

} else if (childDimension == LayoutParams.MATCH_PARENT) {

// Child wants to be our size. So be it.

//  假设我们在布局文件里为 MyCustomTextViewA  设置的宽和高是MATCH_PARENT。那么将会用上文计算出的

//  可用空间(在本demo中为父视图的宽度和高度)和 EXACTLY 组合成一个MeasureSpec并返回

resultSize = size;

resultMode = MeasureSpec.EXACTLY;

} else if (childDimension == LayoutParams.WRAP_CONTENT) {

// Child wants to determine its own size. It can‘t be

//  假设我们在布局文件里为 MyCustomTextViewA  设置的宽和高是WRAP_CONTENT。那么将会用上文计算出的

//  可用空间(在本demo中为父视图的宽度和高度)和 AT_MOST组合成一个MeasureSpec并返回

// bigger than us.

resultSize = size;

resultMode = MeasureSpec.AT_MOST;

}

//  通过以上分析,MyCustomTextViewA  的onMeasure方法接收的參数相应的size不一定是其父视图的宽高

//  能够在布局文件里。将MyCustomTextViewA 的layout_width
改为"wrap_content"或详细的一个dp值

//  再复写MyCustomTextViewA 的onMeasure方法,进行验证

break;

}

return MeasureSpec.makeMeasureSpec(resultSize, resultMode);

}

//  在这里,将以上文得到的 childWidthMeasureSpec 和 childHeightMeasureSpec 作为參数,运行child.measure方法

child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

//  对于ViewGroup类型的视图来讲,接着遍历。运行上文的操作

//  对于非ViewGroup类型的MyCustomTextViewA 来讲。则是调用view类的onMeasure方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),

getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {

//  简单来讲就是将计算得到的详细尺寸赋给成员变量mMeasuredWidth 和 mMeasuredHeight

mMeasuredWidth = measuredWidth;

mMeasuredHeight = measuredHeight;

}

}

}

}

    } // for循环结束

}

以上就是view的measure的大致流程了

简单来说measure过程的作用就是:

在Activity 的 setContentView() 方法运行后,从应用程序窗体的根view開始。依据屏幕分辨率、我们选择的窗体布局(是否全屏等)、xml文件里的layout_width、layout_height(可能为详细的值。也可能为match_parent或者wrap_content)、layout_weight、margin和padding的值进行递归操作,计算出每个view的宽和高,并设置给成员变量mMeasuredWidth
和 mMeasuredHeight 的过程。

所以。我们自己定义view对象,重写onMeasure方法的目的就是依照我们的方式计算并设置view对象的宽和高,比方:

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

setMeasuredDimension(201, 801);

}

这种话就把View默认的measure流程覆盖掉了,无论在布局文件里定义的大小是多少,终于在界面上显示的大小都将会是201*801.

但是,上述代码的意义是什么呢?或者说在什么情况下我们须要重写onMeasure方法呢?

在不想要遵循view的默认measure流程时,或者须要动态调整布局。不太easy在xml文件里操作时,

我们能够考虑重写view的onMeasure方法。

在上文中。我们提到,view的measure过程使用到了xml文件里的 layout_width、layout_height、layout_weight、margin和padding这些属性。那view的绘制过程的第二步layout和第三步draw是否会使用到xml文件里的其它属性呢?回答是肯定的。

尽管measure翻译为測量。layout翻译为布局,但事实上这两个步骤都是在计算,仅仅只是measure过程是在计算view的大小,layout过程是在计算view的坐标而已。view绘制的第二步 —  layout ,核心就是依据xml文件里的gravity属性和第一步 — measure所得到的宽和高的值计算出view对象的坐标。大小和位置都确定了,就能够进行第三步
— draw了。

时间: 2024-10-12 21:08:36

Android - View的绘制流程一(measure)的相关文章

Android View的绘制流程三部曲 —— Measure

在刚开始学习Java的时候,我看的是Mars老师的视频.Mars老师说过的一句话让我印象很深刻:要有一颗面向对象的心. 如果我们用面向对象的思维方式来思考,就会觉的View的绘制机制是很合理,很科学的.我们要在一张纸上画一幅画,首先要测量一下这幅画有多大吧,然后确定在这张纸的哪个地方画会显得比较美观,最后才是用画笔工具将画绘制在纸上. 在Android中也是一样的.View的绘制流程主要是指measure,layout,draw这三步,即测量,布局,绘制.首先是要测量View的宽高,然后布局确定

Android View的绘制流程

View 绘制机制 1. View 树的绘图流程 当 Activity 接收到焦点的时候,它会被请求绘制布局,该请求由 Android framework 处理.绘制是从根节点开始,对布局树进行 measure 和 draw.整个 View 树的绘图流程在ViewRoot.java类的performTraversals()函数展开,该函数所做 的工作可简单概况为是否需要重新计算视图大小(measure).是否需要重新安置视图的位置(layout).以及是否需要重绘(draw),流程图如下: Vi

android view的 绘制流程

韩梦飞沙  韩亚飞  [email protected]  yue31313  han_meng_fei_sha 首先是 从  视图根 这个类的  进行遍历 performTraversals 方法 开始, 然后 经过 测量,布局,绘制, 三个过程 将 视图 绘制出来. 测量  是 测量 视图的 宽高. 布局 是 确定 位置. 绘制 是  绘制.

每日一问:简述 View 的绘制流程

Android 开发中经常需要用一些自定义 View 去满足产品和设计的脑洞,所以 View 的绘制流程至关重要.网上目前有非常多这方面的资料,但最好的方式还是直接跟着源码进行解读,每日一问系列一直追求短平快,所以本文笔者尽量精简. 想必大多数 Android 开发都知道自定义 View 需要关注的几个方法:onMeasure().onLayout() 和 onDraw(),这其实也是每个 View 至关重要的绘制流程. 基本绘制都是会从根视图 ViewRoot 的 performTravers

深入理解 Android 之 View 的绘制流程

概述 本篇文章会从源码(基于Android 6.0)角度分析Android中View的绘制流程,侧重于对整体流程的分析,对一些难以理解的点加以重点阐述,目的是把View绘制的整个流程把握好,而对于特定实现细节则可以日后再对相应源码进行研读.在进行实际的分析之前,我们先来看下面这张图: 我们来对上图做出简单解释:DecorView是一个应用窗口的根容器,它本质上是一个FrameLayout.DecorView有唯一一个子View,它是一个垂直LinearLayout,包含两个子元素,一个是Titl

Android视图的绘制流程(下)——View的Layout与Draw过程

综述 在上篇文章中Android视图的绘制流程(上)--View的测量对View的Measure过程进行了详细的说明.对于在View的绘制的整个过程中,在对View的大小进行测量以后,便开始确定View的位置并且将其绘制到屏幕上.也就是View的Layout与Draw过程.那么就来看一下是如何实现这两个过程的. View的Layout过程 上文提到View的绘制流程是从ViewRoot的performTraversals方法开始,那么在View完成测量以后,在performTraversals方

自定义控件知识储备-View的绘制流程

在自定义控件这个学习系列里,首先写篇文章记录一下View的绘制流程,压压惊:-P.也为以后的自定义控件实践打个基础.虽然讲解View工作流程的文章很多,其中不乏很多精品文章,不过自己能从中理清思路,以自己之言总结出来,也是十分必要的.好的,我要开始装-不,总结了. 1. 前言 当我们打开手机,开始看朋友圈,刷微博的时候,我们有考虑过在我们眼前的一个个View是如何从无到有的展示在我们眼前的么?有考虑过它们的感受么?(神经病才去考虑(ノ??)ノ彡┻━┻--). 当我们在一张纸上画画的时候,哪怕是简

Android View 的测量流程详解

概述 上一篇 Android DecorView 与 Activity 绑定原理分析 分析了在调用 setContentView 之后,DecorView 是如何与 activity 关联在一起的,最后讲到了 ViewRootImpl 开始绘制的逻辑.本文接着上篇,继续往下讲,开始分析 view 的绘制流程. 上文说到了调用 performTraversals 进行绘制,由于 performTraversals 方法比较长,看一个简化版: // ViewRootImpl 类 private vo

Android View 如何绘制

上文说道了Android如何测量,但是一个漂亮的控件我只知道您长到哪儿,这当然不行.只需要简单重写OnDraw方法,并在Canvas(画布)对象上调用那根五颜六色的画笔就能够画出这控件"性感"的外表.那么View又是如何进行绘制了? 要了解View如何绘制,就需要了解canvas(画布)是什么?paint(画笔)能够做什么. Ⅰ.canvas就是表示一块画布,你可以在上面画你所朝思暮想的东西.当我们重写onDraw方法的时候,就能够拿到一个Canvas对象,这个就是你的舞台,画你所思所