简单研究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     }

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     }

可以看到,在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()。

时间: 2024-10-24 09:28:34

简单研究Android View绘制一的相关文章

简单研究Android View绘制三 布局过程

2015-07-28 17:29:19 这一篇主要看看布局过程 一.布局过程肯定要不可避免的涉及到layout()和onLayout()方法,这两个方法都是定义在View.java中,源码如下: 1 /** 2 * Assign a size and position to a view and all of its 3 * descendants 4 * 5 * <p>This is the second phase of the layout mechanism. 6 * (The fir

简单研究Android View绘制二 LayoutParams

2015-07-28 17:23:20 本篇是关于LayoutParams相关 ViewGroup.LayoutParams文档解释如下: LayoutParams are used by views to tell their parents how they want to be laid out. See ViewGroup Layout Attributes for a list of all child view attributes that this class supports.

[Android][转]Android View绘制13问13答

转自:http://www.androidchina.net/4458.html 1.view的绘制流程分几步,从哪开始?哪个过程结束以后能看到view? 答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程.draw流程结束以后就可以在屏幕上看到view了. 2.view的测量宽高和实际宽高有区别吗? 答:基本上百分之99的情况下都是可以认为没有区别的.有两种情况,有区别.第一种 就是有的时候会因为某些原因 view会多次测量,那

Android View绘制及实践

概述 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为: - 判断是否需要重新计算视图大小(measure) - 判断是否重新需要安置视图的位置(layout) - 判断是否需要重绘(draw) 其整个流程图如下: 图片来自:Android 开源项目源码解析 公共技术点中的 View 绘制流程 在Android中View的整个生命周期,调用invalidate和requestLayout会触发一系列的方法,

Android View 绘制流程(Draw) 完全解析

前言 前几篇文章,笔者分别讲述了DecorView,measure,layout流程等,接下来将详细分析三大工作流程的最后一个流程--绘制流程.测量流程决定了View的大小,布局流程决定了View的位置,那么绘制流程将决定View的样子,一个View该显示什么由绘制流程完成.以下源码均取自Android API 21. 从performDraw说起 前面几篇文章提到,三大工作流程始于ViewRootImpl#performTraversals,在这个方法内部会分别调用performMeasure

Android View绘制机制

------------------------------------------------------------------------------ GitHub:lightSky    微博:    light_sky, 即时分享最新技术,欢迎关注 ------------------------------------------------------------------------------ 前言 该篇文章来自一个开源项目android-open-project-analy

Android View绘制流程

框架分析 在之前的下拉刷新中,小结过触屏消息先到WindowManagerService(Wms)然后顺次传递给ViewRoot(派生自Handler),经decor view到Activity再传递给指定的View,这次整理View的绘制流程,通过源码可知,这个过程应该没有涉及到IPC(或者我没有发现),需要绘制时在UI线程中通过ViewRoot发送一个异步请求消息,然后ViewRoot自己接收并不处理这个消息. 在正式进入View绘制之前,首先需要明确一下Android UI的架构组成,偷图

Android View 绘制过程

Android的View绘制是从根节点(Activity是DecorView)开始,他是一个自上而下的过程.View的绘制经历三个过程:Measure.Layout.Draw.基本流程如下图: performTraversals函数,具体的可以参考一下源代码: 1 private void performTraversals() { 2 final View host = mView; 3 ... 4 host.measure(childWidthMeasureSpec, childHeight

对于Android View绘制的一些思考

AT_MOST 表示最大是多大. UNSPECIFIED 不确定是多大, 你想多大就多大,我尽量满足你. EXACTLY 就这么大 已经指定了大小 MATCH_PARENT 为什么说MATCH_PARENT 因为 MATCH_PARENT 就是间接说 我要占据父控件剩下的那部分了. 这就相当于指定了确定的宽或高. 一个view 的绘制需要三个阶段 measure -> layout -> draw view的测量阶段 measure-> onMeasure() 我们就看单纯一个view的