• Android


    该博文所用的demo结构图:
    Android - View的绘制流程(一) - H , W , G , T ... .. - H , W , G , T ... ..
    相应的代码:
    MainActivity.java:
    [java] view plain copy
     在CODE上查看代码片派生到我的代码片
    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
     在CODE上查看代码片派生到我的代码片
    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
     在CODE上查看代码片派生到我的代码片
    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
     在CODE上查看代码片派生到我的代码片
    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
     在CODE上查看代码片派生到我的代码片
    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
     在CODE上查看代码片派生到我的代码片
    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的效果图和其涉及到的相关布局的关系例如以下图:
    Android - View的绘制流程(一) - H , W , G , T ... .. - H , W , G , T ... ..
    紫色框代表根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 - View的绘制流程(一) - H , W , G , T ... .. - H , W , G , T ... ..
    (该图截取自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的一个静态内部类,看它的凝视:
    Android - View的绘制流程(一) - H , W , G , T ... .. - H , W , G , T ... ..
     一个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):
    Android - View的绘制流程(一) - H , W , G , T ... .. - H , W , G , T ... ..
    在设置全屏和非全屏的情况下,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
     在CODE上查看代码片派生到我的代码片
    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了。

  • 相关阅读:
    Spring Boot 快速入门
    mySql 主从复制linux配置
    Neural Networks for Machine Learning by Geoffrey Hinton (1~2)
    DIV浮动IE文本产生3象素的bug
    Leet Code OJ 338. Counting Bits [Difficulty: Medium]
    Unity3D:Gizmos画圆(原创)
    科学世界的人文关怀:开源科学与人工智能
    使用c#訪问Access数据库时,提示找不到可安装的 ISAM
    Android API Guides---Services
    《从0到1》读书笔记第一章&quot;未来的挑战&quot;第2记:做老子还是做孙子
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7390390.html
Copyright © 2020-2023  润新知