zoukankan      html  css  js  c++  java
  • 简单研究Android View绘制一 测量过程

    2015-07-27 16:52:58

    一、如何通过继承ViewGroup来实现自定义View?首先得搞清楚Android时如何绘制View的,参考Android官方文档:How Android Draws Views

    以下翻译摘自:http://blog.csdn.net/linghu_java/article/details/23882681,这也是一片好文章,推荐大家看看~

    When an Activity receives focus, it will be requested to draw its layout. The Android framework will handle the procedure for drawing, but the Activity must provide the root node of its layout hierarchy.

    当一个Activity获取焦点的时候,它就会被要求去画出它的布局。Android框架会处理绘画过程,但是Activity必须提供布局的根节点,在上面的图中,我们可以理解为最上面的ViewGroup,而实际上还有一个更深的root view。

    Drawing begins with the root node of the layout. It is requested to measure and draw the layout tree. Drawing is handled by walking the tree and rendering each View that intersects the invalid region. In turn, each View group is responsible for requesting each of its children to be drawn (with the draw() method) and each View is responsible for drawing itself. Because the tree is traversed in-order, this means that parents will be drawn before (i.e., behind) their children, with siblings drawn in the order they appear in the tree.

    绘画开始于布局的根节点,要求测量并且画出整个布局树。绘画通过遍历整个树来完成,不可见的区域的View被放弃。每个ViewGroup负责要求它的子View去绘画,每个子View则负责去绘画自己。因为布局树是顺序遍历的,这意味着父View在子View之前被画出来(这个符合常理,后面解释)。

    注解:假设一个TextView设置为(FILL_PAREMT, FILL_PARENT),则很明显必须先画出父View的尺寸,才能去画出这个TextView,而且从上至下也就是先画父View再画子View,显示的时候才正常,否则父View会挡住子View的显示。说白了,不是子View你想要多大就能有多大的空间给你,前提是父View得先知道自己有权利使用多大的空间。

    Drawing the layout is a two pass process: a measure pass and a layout pass. The measuring pass is implemented in measure(int, int) and is a top-down traversal of the View tree. Each View pushes dimension specifications down the tree during the recursion. At the end of the measure pass, every View has stored its measurements. The second pass happens in layout(int, int, int, int) and is also top-down. During this pass each parent is responsible for positioning all of its children using the sizes computed in the measure pass.

    布局绘画涉及两个过程:测量过程和布局过程。测量过程通过measure方法实现,是View树自顶向下的遍历,每个View在循环过程中将尺寸细节往下传递,当测量过程完成之后,所有的View都存储了自己的尺寸。第二个过程则是通过方法layout来实现的,也是自顶向下的。在这个过程中,每个父View负责通过计算好的尺寸放置它的子View。

    注解:这和前面说的一样,一个过程是用来丈量尺寸的,一个过程是用来摆放位置的。

    When a View's measure() method returns, its getMeasuredWidth() and getMeasuredHeight() values must be set, along with those for all of that View's descendants. A View's measured width and measured height values must respect the constraints imposed by the View's parents. This guarantees that at the end of the measure pass, all parents accept all of their children's measurements. A parent View may call measure() more than once on its children. For example, the parent may measure each child once with unspecified dimensions to find out how big they want to be, then call measure() on them again with actual numbers if the sum of all the children's unconstrained sizes is too big or too small (i.e., if the children don't agree among themselves as to how much space they each get, the parent will intervene and set the rules on the second pass).

    当一个View的measure()方法返回的时候,它的getMeasuredWidth和getMeasuredHeight方法的值一定是被设置好的。它所有的子节点同样被设置好。一个View的测量宽和测量高一定要遵循父View的约束,这保证了在测量过程结束的时候,所有的父View可以接受子View的测量值。一个父View或许会多次调用子View的measure()方法。举个例子,父View会使用不明确的尺寸去丈量看看子View到底需要多大,当子View总的尺寸太大或者太小的时候会再次使用实际的尺寸去调用onmeasure().

    二、结合自己写的代码框架,分析一下绘制的过程

    这是一个布局文件:

     1 <com.test.touch.MyLinear2 xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:layout_width="300dp"
     3     android:layout_height="200dp"
     4     android:paddingTop="22dp"
     5     android:layout_marginLeft="10dp" >
     6 
     7     <TextView
     8         android:id="@+id/tv1"
     9         android:layout_width="match_parent"
    10         android:layout_height="100dp"
    11         android:background="#f00"
    12         android:layout_marginLeft="10dp"
    13         android:text="Hello World Text" />
    14 
    15     <com.test.touch.MyTextView
    16         android:layout_width="match_parent"
    17         android:layout_height="100dp"
    18         android:background="#0f0"
    19         android:paddingTop="22dp"
    20         android:text="Hello World" />
    21 
    22 </com.test.touch.MyLinear2>

    MyLinear2.java代码框架:

     1 public class MyLinear2 extends ViewGroup {
     2     private static final String TAG = "David_MyLinear2";
     3 
     4     public MyLinear2(Context context) {
     5         super(context);
     6     }
     7 
     8     public MyLinear2(Context context, AttributeSet attrs) {
     9         super(context, attrs);
    10     }
    11 
    12     @Override
    13     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    14         
    15     }
    16 
    17     @Override
    18     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
    19         Log.e(TAG, "generateLayoutParams attrs");
    20         return new MarginLayoutParams(getContext(), attrs);
    21     }
    22 
    23     @Override
    24     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
    25         Log.e(TAG, "generateDefaultLayoutParams");
    26         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    27     }
    28 
    29     @Override
    30     protected boolean checkLayoutParams(LayoutParams p) {
    31         return super.checkLayoutParams(p);
    32     }
    33 
    34     @Override
    35     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
    36         Log.e(TAG, "generateLayoutParams p");
    37         return new MarginLayoutParams(p);
    38     }
    39 
    40     @Override
    41     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    42         int measuredHeight = measureHeight(heightMeasureSpec);
    43         int measuredWidth = measureWidth(widthMeasureSpec);
    44         Log.e(TAG, "onMeasure measuredHeight = " + measuredHeight);
    45         Log.e(TAG, "onMeasure measuredWidth = " + measuredWidth);
    46         setMeasuredDimension(measuredWidth, measuredHeight);
    47         measureChildren(widthMeasureSpec, heightMeasureSpec);
    48     }
    49 
    50     private int measureHeight(int measureSpec) {
    51 
    52         int specMode = MeasureSpec.getMode(measureSpec);
    53         int specSize = MeasureSpec.getSize(measureSpec);
    54 
    55         // Default size if no limits are specified.
    56 
    57         int result = 500;
    58         if (specMode == MeasureSpec.AT_MOST){
    59             // Calculate the ideal size of your
    60             // control within this maximum size.
    61             // If your control fills the available
    62             // space return the outer bound.
    63 
    64             result = specSize;
    65         } else if (specMode == MeasureSpec.EXACTLY){
    66             // If your control can fit within these bounds return that value.
    67             result = specSize;
    68         }
    69         return result;
    70     }
    71 
    72     private int measureWidth(int measureSpec) {
    73         int specMode = MeasureSpec.getMode(measureSpec);
    74         int specSize = MeasureSpec.getSize(measureSpec);
    75 
    76         // Default size if no limits are specified.
    77         int result = 500;
    78         if (specMode == MeasureSpec.AT_MOST){
    79             // Calculate the ideal size of your control
    80             // within this maximum size.
    81             // If your control fills the available space
    82             // return the outer bound.
    83             result = specSize;
    84         } else if (specMode == MeasureSpec.EXACTLY){
    85             // If your control can fit within these bounds return that value.
    86             result = specSize;
    87         }
    88         return result;
    89     }
    90 }

    MyTextView.java代码框架:

     1 public class MyTextView extends TextView {
     2     private static final String TAG = "David__MyTextView";
     3 
     4     public MyTextView(Context context, AttributeSet attrs) {
     5         super(context, attrs);
     6     }
     7 
     8     public MyTextView(Context context) {
     9         super(context);
    10     }
    11 
    12     @Override
    13     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    14 
    15         int measuredHeight = measureHeight(heightMeasureSpec);
    16         int measuredWidth = measureWidth(widthMeasureSpec);
    17         Log.e(TAG, "measuredHeight = " + measuredHeight);
    18         Log.e(TAG, "measuredWidth = " + measuredWidth);
    19         setMeasuredDimension(measuredWidth, measuredHeight);
    20     }
    21 
    22     private int measureHeight(int measureSpec) {
    23         ***
    24     }
    25 
    26     private int measureWidth(int measureSpec) {
    27         ***
    28     }
    29 }

    既然前面说到View的绘制涉及到两个过程:测量过程和布局过程,而且他们是有先后顺序的,那么我们先分析测量过程。

    三、测量过程

    View.java中有measure()、onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,measure()被设计成了final方法,这就意味着此方法不允许被重写。measure()中调用了onMeasure()方法,所以onMeasure()是可以用来被子类重写的,源码如下,有兴趣的可以展开看看。

    measure()源码:

     1 /**
     2      * <p>
     3      * This is called to find out how big a view should be. The parent
     4      * supplies constraint information in the width and height parameters.
     5      * </p>
     6      *
     7      * <p>
     8      * The actual measurement work of a view is performed in
     9      * {@link #onMeasure(int, int)}, called by this method. Therefore, only
    10      * {@link #onMeasure(int, int)} can and must be overridden by subclasses.
    11      * </p>
    12      *
    13      *
    14      * @param widthMeasureSpec Horizontal space requirements as imposed by the
    15      *        parent
    16      * @param heightMeasureSpec Vertical space requirements as imposed by the
    17      *        parent
    18      *
    19      * @see #onMeasure(int, int)
    20      */
    21     public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    22         if (DBG_SYSTRACE_MEASURE){
    23             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure : " + getClass().getSimpleName());
    24         }
    25         boolean optical = isLayoutModeOptical(this);
    26         if (optical != isLayoutModeOptical(mParent)) {
    27             Insets insets = getOpticalInsets();
    28             int oWidth  = insets.left + insets.right;
    29             int oHeight = insets.top  + insets.bottom;
    30             widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
    31             heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
    32         }
    33         if (DBG_LAYOUT) {
    34             Xlog.d(VIEW_LOG_TAG, "veiw measure start, this =" + this + ", widthMeasureSpec = " +
    35                 MeasureSpec.toString(widthMeasureSpec) + ", heightMeasureSpec = " + MeasureSpec.toString(heightMeasureSpec)
    36                 + ", mOldWidthMeasureSpec = " + MeasureSpec.toString(mOldWidthMeasureSpec) + ", mOldHeightMeasureSpec = "
    37                 + MeasureSpec.toString(mOldHeightMeasureSpec));
    38         }
    39 
    40         // Suppress sign extension for the low bytes
    41         long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
    42         if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
    43 
    44         if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
    45                 widthMeasureSpec != mOldWidthMeasureSpec ||
    46                 heightMeasureSpec != mOldHeightMeasureSpec) {
    47 
    48             // first clears the measured dimension flag
    49             mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
    50 
    51             resolveRtlPropertiesIfNeeded();
    52 
    53             int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
    54                     mMeasureCache.indexOfKey(key);
    55             if (cacheIndex < 0 || sIgnoreMeasureCache) {
    56                 // measure ourselves, this should set the measured dimension flag back
    57                 /// M: Monitor onMeasue time if longer than 3s print log.
    58                 long logTime = System.currentTimeMillis();
    59                 onMeasure(widthMeasureSpec, heightMeasureSpec);
    60                 long nowTime = System.currentTimeMillis();
    61                 if (nowTime - logTime > DBG_TIMEOUT_VALUE) {
    62                     Xlog.d(VIEW_LOG_TAG, "[ANR Warning]onMeasure time too long, this =" + this + "time =" + (nowTime - logTime));
    63                 }
    64                 if (DBG_LAYOUT) {
    65                     Xlog.d(VIEW_LOG_TAG, "veiw measure end, this =" + this + ", mMeasuredWidth = "
    66                         + mMeasuredWidth + ", mMeasuredHeight = " + mMeasuredHeight + ", time =" + (nowTime - logTime) + " ms");
    67                 }
    68                 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    69             } else {
    70                 long value = mMeasureCache.valueAt(cacheIndex);
    71                 // Casting a long to int drops the high 32 bits, no mask needed
    72                 setMeasuredDimension((int) (value >> 32), (int) value);
    73                 mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
    74             }
    75 
    76             // flag not set, setMeasuredDimension() was not invoked, we raise
    77             // an exception to warn the developer
    78             if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
    79                 throw new IllegalStateException("onMeasure() did not set the"
    80                         + " measured dimension by calling"
    81                         + " setMeasuredDimension()");
    82             }
    83 
    84             mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
    85         }
    86 
    87         mOldWidthMeasureSpec = widthMeasureSpec;
    88         mOldHeightMeasureSpec = heightMeasureSpec;
    89 
    90         mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
    91                 (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
    92         if (DBG_SYSTRACE_MEASURE){
    93             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    94         }
    95     }
    View Code

    onMeasure(int widthMeasureSpec, int heightMeasureSpec)源码:

     1 /**
     2      * <p>
     3      * Measure the view and its content to determine the measured width and the
     4      * measured height. This method is invoked by {@link #measure(int, int)} and
     5      * should be overriden by subclasses to provide accurate and efficient
     6      * measurement of their contents.
     7      * </p>
     8      *
     9      * <p>
    10      * <strong>CONTRACT:</strong> When overriding this method, you
    11      * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the
    12      * measured width and height of this view. Failure to do so will trigger an
    13      * <code>IllegalStateException</code>, thrown by
    14      * {@link #measure(int, int)}. Calling the superclass'
    15      * {@link #onMeasure(int, int)} is a valid use.
    16      * </p>
    17      *
    18      * <p>
    19      * The base class implementation of measure defaults to the background size,
    20      * unless a larger size is allowed by the MeasureSpec. Subclasses should
    21      * override {@link #onMeasure(int, int)} to provide better measurements of
    22      * their content.
    23      * </p>
    24      *
    25      * <p>
    26      * If this method is overridden, it is the subclass's responsibility to make
    27      * sure the measured height and width are at least the view's minimum height
    28      * and width ({@link #getSuggestedMinimumHeight()} and
    29      * {@link #getSuggestedMinimumWidth()}).
    30      * </p>
    31      *
    32      * @param widthMeasureSpec horizontal space requirements as imposed by the parent.
    33      *                         The requirements are encoded with
    34      *                         {@link android.view.View.MeasureSpec}.
    35      * @param heightMeasureSpec vertical space requirements as imposed by the parent.
    36      *                         The requirements are encoded with
    37      *                         {@link android.view.View.MeasureSpec}.
    38      *
    39      * @see #getMeasuredWidth()
    40      * @see #getMeasuredHeight()
    41      * @see #setMeasuredDimension(int, int)
    42      * @see #getSuggestedMinimumHeight()
    43      * @see #getSuggestedMinimumWidth()
    44      * @see android.view.View.MeasureSpec#getMode(int)
    45      * @see android.view.View.MeasureSpec#getSize(int)
    46      */
    47     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    48         setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
    49                 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    50     }
    51 
    52     /**
    53      * <p>This method must be called by {@link #onMeasure(int, int)} to store the
    54      * measured width and measured height. Failing to do so will trigger an
    55      * exception at measurement time.</p>
    56      *
    57      * @param measuredWidth The measured width of this view.  May be a complex
    58      * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
    59      * {@link #MEASURED_STATE_TOO_SMALL}.
    60      * @param measuredHeight The measured height of this view.  May be a complex
    61      * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
    62      * {@link #MEASURED_STATE_TOO_SMALL}.
    63      */
    64     protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    65         boolean optical = isLayoutModeOptical(this);
    66         if (optical != isLayoutModeOptical(mParent)) {
    67             Insets insets = getOpticalInsets();
    68             int opticalWidth  = insets.left + insets.right;
    69             int opticalHeight = insets.top  + insets.bottom;
    70 
    71             measuredWidth  += optical ? opticalWidth  : -opticalWidth;
    72             measuredHeight += optical ? opticalHeight : -opticalHeight;
    73         }
    74         mMeasuredWidth = measuredWidth;
    75         mMeasuredHeight = measuredHeight;
    76 
    77         mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    78     }
    View Code

    可以看到,在onMeasure()方法中调用了setMeasuredDimension()方法,其中mMeasuredWidth和mMeasuredHeight都已经被计算好了,而这两个值就是以下方法的返回值,很重要:

    1 public final int getMeasuredWidth() {
    2     return mMeasuredWidth & MEASURED_SIZE_MASK;
    3 }
    4 
    5 public final int getMeasuredHeight() {
    6     return mMeasuredHeight & MEASURED_SIZE_MASK;
    7 }

    OK,这两个测量相关的重要方法我们已经知道他们的来历了,那么看看ViewGroup中是怎么使用他们的。

    首先,因为measure()方法是不能被重写的,所以ViewGroup中并没有重写此方法,而onMeasure()方法呢,ViewGroup也并没有重写,但是ViewGroup的子类却全部重写了onMeasure()方法,比如LinearLayout,如下:

    1 @Override
    2     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    3         int level = getCurrentLevel();
    4         if (mOrientation == VERTICAL) {
    5             measureVertical(widthMeasureSpec, heightMeasureSpec);
    6         } else {
    7             measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    8         }
    9     }

    我们再简单的看看measureVertical(int widthMeasureSpec, int heightMeasureSpec)方法中干了些什么事情,源码很多,就不贴出来了,但基本是计算竖向布局下如何计算并设置自身和子View所需要的space,其中调用了一句很重要的代码,是用来计算每一个子View所需的space的,如下:

    1     // Determine how big this child would like to be. If this or
    2     // previous children have given a weight, then we allow it to
    3     // use all available space (and we will shrink things later
    4     // if needed).
    5     measureChildBeforeLayout(
    6            child, i, widthMeasureSpec, 0, heightMeasureSpec,
    7            totalWeight == 0 ? mTotalLength : 0);

    接着看measureChildBeforeLayout()方法:

    1 void measureChildBeforeLayout(View child, int childIndex,
    2            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
    3            int totalHeight) {
    4            measureChildWithMargins(child, widthMeasureSpec, totalWidth,
    5                   heightMeasureSpec, totalHeight);
    6 }

    measureChildWithMargins()方法并不是LinearLayout类的方法,而是ViewGroup的方法,源码如下:

     1 protected void measureChildWithMargins(View child,
     2             int parentWidthMeasureSpec, int widthUsed,
     3             int parentHeightMeasureSpec, int heightUsed) {
     4         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
     5 
     6         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
     7                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
     8                         + widthUsed, lp.width);
     9         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
    10                 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
    11                         + heightUsed, lp.height);
    12         int level = -1;
    13         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    14     }

    请注意,红色的这行代码很重要哦,在LinearLayout的onMeasure()方法中,遍历每一个子View,然后计算每个子View的measure()方法来计算子View需要的space。我们前面说过,View的measure()方法调用了onMeasure()方法,这就是说,我们在实现自己的View时所重写的onMeasure()方法会在此时被调用。而调用onMeasure()方法后,子View的mMeasuredWidth和mMeasuredHeight等值就已经被计算出来了。结合前面的Demo代码,日志如下:

     1 07-27 15:37:24.196 E/David_MyLinear2(18290): onMeasure measuredHeight = 600
     2 07-27 15:37:24.196 E/David_MyLinear2(18290): onMeasure measuredWidth = 900
     3 07-27 15:37:24.196 E/David___MyTextView(18290): onMeasure measuredHeight = 300
     4 07-27 15:37:24.196 E/David___MyTextView(18290): onMeasure measuredWidth = 900
     5 ===========================================
     6 07-27 15:37:24.306 E/David_MyLinear2(18290): onMeasure measuredHeight = 600
     7 07-27 15:37:24.306 E/David_MyLinear2(18290): onMeasure measuredWidth = 900
     8 07-27 15:37:24.306 E/David___MyTextView(18290): onMeasure measuredHeight = 300
     9 07-27 15:37:24.306 E/David___MyTextView(18290): onMeasure measuredWidth = 900
    10 ===========================================
    11 07-27 15:37:26.436 E/David_MyLinear2(18290): onMeasure measuredHeight = 600
    12 07-27 15:37:26.436 E/David_MyLinear2(18290): onMeasure measuredWidth = 900
    13 07-27 15:37:26.436 E/David___MyTextView(18290): onMeasure measuredHeight = 300
    14 07-27 15:37:26.436 E/David___MyTextView(18290): onMeasure measuredWidth = 900

    可见,onMeasure方法被多次调用,而且先调用ViewGroup子类的onMeasure方法,然后才是View子类的onMeasure方法,至于为什么MyLinear2的measuredHeight=600和measuredWidth=900,以及MyTextView的measuredHeight=300和measuredWidth=900,请看布局文件:

     1 <com.test.touch.MyLinear2 xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:layout_width="300dp"
     3     android:layout_height="200dp"
     4     android:paddingTop="22dp"
     5     android:layout_marginLeft="10dp" >
     6 
     7     <TextView
     8         android:id="@+id/tv1"
     9         android:layout_width="match_parent"
    10         android:layout_height="100dp"
    11         android:background="#f00"
    12         android:layout_marginLeft="10dp"
    13         android:text="Hello World Text" />
    14 
    15     <com.test.touch.MyTextView
    16         android:layout_width="match_parent"
    17         android:layout_height="100dp"
    18         android:background="#0f0"
    19         android:paddingTop="22dp"
    20         android:text="Hello World" />
    21 
    22 </com.test.touch.MyLinear2>

    我是在1080P的手机上运行的,desenty为3,所以MyLinear2的宽为900px,高为600。至于MyTextView的高为300px,这很好理解,但是宽度为什么时900px呢?我这值得明明是match_parent,应该是1080px啊?前面说过了,不是你子View想要多大的空间就能要多大。这还取决于父View有权力使用多大的空间,在这个例子中,父View能使用的最大宽度是900px,所以你子View能使用的最大宽度撑死了不会超过900px啊~

    至此,简单的总结一下测量过程:

    Android View绘制系统首先取到Activity布局的根View,当然这一般是一个ViewGroup了,然后调用ViewGroup的measure()方法,我们知道,ViewGroup没有自己的measure方法,事实上任何一个子View都不会有自己的measure()方法的,在View中被设置成了final了嘛~所以其实调用的是View的measure方法,measure()会调用子类的onMeasure()方法,所以这个onMeasure方法我们需要给出自己的实现。
    ViewGroup子类的onMeasure方法中我们必须得显式的调用ViewGroup的measureChildWithMargins()或者measureChildren(widthMeasureSpec, heightMeasureSpec);这两个方法最大的区别是measureChildWithMargins()必须得传入子View,同时会计算子View的padding和margin值,而measureChildren()会自己循环取出每一个子View。共同点是都会调用子View的measure()方法,也就调到子View的onMeasure()方法了。


    所以总的大致流程是:Android UI绘制系统 --> ViewGroup的measure() --> ViewGroup的onMeasure() --> [必须得写代码,主动计算每一个子View所需的空间,同时传入ViewGroup自身(widthMeasureSpec, heightMeasureSpec),限制子View计算自身所需space] --> ViewGroup中子View的measure() --> ViewGroup中子View的onMeasure()。

  • 相关阅读:
    oracle中获取当前整点和上一个小时整点,日期类型
    MYSQL中替换oracle中runum用法
    oracle 中备份表
    發生了不愉快的事情
    今年下雪了。。。
    VB.net下非常好用的实现FTP的类
    今年過節不回家了
    焕肤:不要暗沉
    不要打梦到的电话号码。。。
    關於IT職業的思考
  • 原文地址:https://www.cnblogs.com/wlrhnh/p/4680636.html
Copyright © 2011-2022 走看看