转自:http://www.androidchina.net/4458.html
1.view的绘制流程分几步,从哪开始?哪个过程结束以后能看到view?
答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程。draw流程结束以后就可以在屏幕上看到view了。
2.view的测量宽高和实际宽高有区别吗?
答:基本上百分之99的情况下都是可以认为没有区别的。有两种情况,有区别。第一种 就是有的时候会因为某些原因 view会多次测量,那第一次测量的宽高 肯定和最后实际的宽高 是不一定相等的,但是在这种情况下
最后一次测量的宽高和实际宽高是一致的。此外,实际宽高是在layout流程里确定的,我们可以在layout流程里 将实际宽高写死 写成硬编码,这样测量的宽高和实际宽高就肯定不一样了,虽然这么做没有意义 而且也不好。
3.view的measureSpec 由谁决定?顶级view呢?
答:由view自己的layoutparams和父容器 一起决定自己的measureSpec。一旦确定了spec,onMeasure中就可以确定view的宽高了。
顶级view就稍微特殊一点,对于decorView的测量在ViewRootImpl的源码里。
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
 | 
//desire的这2个参数就代表屏幕的宽高,  childWidthMeasureSpec
 = getRootMeasureSpec(desiredWindowWidth, lp.width);  childHeightMeasureSpec
 = getRootMeasureSpec(desiredWindowHeight, lp.height);  performMeasure(childWidthMeasureSpec,
 childHeightMeasureSpec);  //decorView的measureSpec就是在这里确定的,其实比普通view的measurespec要简单的多  //代码就不分析了
 一目了然的东西  private
static 
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);            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;} | 
4.对于普通view来说,他的measure过程中,与父view有关吗?如果有关,这个父view也就是viewgroup扮演了什么角色?
答:看源码:
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
97 
98 
99 
100 
101 
 | 
//对于普通view的measure来说
 是由这个view的 父view ,也就是viewgroup来触发的。//也就是下面这个measureChildWithMargins方法protected
void 
measureChildWithMargins(View child,            int
parentWidthMeasureSpec, int
widthUsed,            int
parentHeightMeasureSpec, int
heightUsed) {         //第一步
 先取得子view的 layoutParams 参数值        final
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();        //然后开始计算子view的spec的值,注意这里看到
 计算的时候除了要用子view的 layoutparams参数以外        //还用到了父view
 也就是viewgroup自己的spec的值        final
int 
childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,                mPaddingLeft
 + mPaddingRight + lp.leftMargin + lp.rightMargin                        +
 widthUsed, lp.width);        final
int 
childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,                mPaddingTop
 + mPaddingBottom + lp.topMargin + lp.bottomMargin                        +
 heightUsed, lp.height);        child.measure(childWidthMeasureSpec,
 childHeightMeasureSpec);}//这个算view的spec的方法
 看上去一大串 但是真的逻辑非常简单 就是根据父亲viewgroup//的meaurespec
 同时还有view自己的params来确定 view自己的measureSpec。//注意这里的参数是padding,这个值的含义是
 父容器已占用的控件的大小 所以view的Specsize//的值
 你们可以看到 是要减去这个padding的值的。总大小-已经用的 =可用的。 很好理解。//然后就是下面的switch逻辑
 要自己梳理清楚。其实也不难,主要是下面几条原则//如果view采用固定宽高,也就是写死的数值那种。那就不管父亲的spec的值了,view的spec
 就肯定是exactly 并且大小遵循layout参数里设置的大小。//如果view的宽高是match_parent
 ,那么就要看父容器viewgroup的 spec的值了,如果父view的spec是exactly模式,//那view也肯定是exactly,并且大小就是父容器剩下的空间。如果父容器是at_most模式,那view也是at_most
 并且不会超过剩余空间大小//如果view的宽高是wrap_content,
 那就不管父容器的spec了,view的spec一定是at_most 并且不会超过父view 剩余空间的大小。public
static 
int 
getChildMeasureSpec(int
spec, int
padding, int
childDimension) {        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        case
MeasureSpec.EXACTLY:            if
(childDimension >= 0)
 {                resultSize
 = childDimension;                resultMode
 = MeasureSpec.EXACTLY;            } else
if 
(childDimension == LayoutParams.MATCH_PARENT) {                //
 Child wants to be our size. So be it.                resultSize
 = size;                resultMode
 = MeasureSpec.EXACTLY;            } else
if 
(childDimension == LayoutParams.WRAP_CONTENT) {                //
 Child wants to determine its own size. It can‘t be                //
 bigger than us.                resultSize
 = size;                resultMode
 = MeasureSpec.AT_MOST;            }            break;        //
 Parent has imposed a maximum size on us        case
MeasureSpec.AT_MOST:            if
(childDimension >= 0)
 {                //
 Child wants a specific size... so be it                resultSize
 = childDimension;                resultMode
 = MeasureSpec.EXACTLY;            } else
if 
(childDimension == LayoutParams.MATCH_PARENT) {                //
 Child wants to be our size, but our size is not fixed.                //
 Constrain child to not be bigger than us.                resultSize
 = size;                resultMode
 = MeasureSpec.AT_MOST;            } else
if 
(childDimension == LayoutParams.WRAP_CONTENT) {                //
 Child wants to determine its own size. It can‘t be                //
 bigger than us.                resultSize
 = size;                resultMode
 = MeasureSpec.AT_MOST;            }            break;        //
 Parent asked to see how big we want to be        case
MeasureSpec.UNSPECIFIED:            if
(childDimension >= 0)
 {                //
 Child wants a specific size... let him have it                resultSize
 = childDimension;                resultMode
 = MeasureSpec.EXACTLY;            } else
if 
(childDimension == LayoutParams.MATCH_PARENT) {                //
 Child wants to be our size... find out how big it should                //
 be                resultSize
 = View.sUseZeroUnspecifiedMeasureSpec ? 0
: size;                resultMode
 = MeasureSpec.UNSPECIFIED;            } else
if 
(childDimension == LayoutParams.WRAP_CONTENT) {                //
 Child wants to determine its own size.... find out how                //
 big it should be                resultSize
 = View.sUseZeroUnspecifiedMeasureSpec ? 0
: size;                resultMode
 = MeasureSpec.UNSPECIFIED;            }            break;        }        return
MeasureSpec.makeMeasureSpec(resultSize, resultMode);    } | 
5.view的meaure和onMeasure有什么关系?
答:看源码:
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
 | 
//view的measure是final
 方法 我们子类无法修改的。 public
final 
void 
measure(int
widthMeasureSpec, int
heightMeasureSpec) {        boolean
optical = isLayoutModeOptical(this);        if
(optical != isLayoutModeOptical(mParent)) {            Insets
 insets = getOpticalInsets();            int
oWidth  = insets.left + insets.right;            int
oHeight = insets.top  + insets.bottom;            widthMeasureSpec 
 = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);            heightMeasureSpec
 = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);        }        //
 Suppress sign extension for the low bytes        long
key = (long)
 widthMeasureSpec << 32
| (long)
 heightMeasureSpec & 0xffffffffL;        if
(mMeasureCache == null)
 mMeasureCache = new
LongSparseLongArray(2);        if
((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||                widthMeasureSpec
 != mOldWidthMeasureSpec ||                heightMeasureSpec
 != mOldHeightMeasureSpec) {            //
 first clears the measured dimension flag            mPrivateFlags
 &= ~PFLAG_MEASURED_DIMENSION_SET;            resolveRtlPropertiesIfNeeded();            int
cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1
:                    mMeasureCache.indexOfKey(key);            if
(cacheIndex < 0
|| sIgnoreMeasureCache) {                //
 measure ourselves, this should set the measured dimension flag back                onMeasure(widthMeasureSpec,
 heightMeasureSpec);                mPrivateFlags3
 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            } else
{                long
value = mMeasureCache.valueAt(cacheIndex);                //
 Casting a long to int drops the high 32 bits, no mask needed                setMeasuredDimensionRaw((int)
 (value >> 32),
 (int)
 value);                mPrivateFlags3
 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            }            //
 flag not set, setMeasuredDimension() was not invoked, we raise            //
 an exception to warn the developer            if
((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {                throw
new 
IllegalStateException("View
 with id " 
+ getId() + ":
 "                        +
 getClass().getName() + "#onMeasure()
 did not set the"                        + "
 measured dimension by calling"                        + "
 setMeasuredDimension()");            }            mPrivateFlags
 |= PFLAG_LAYOUT_REQUIRED;        }        mOldWidthMeasureSpec
 = widthMeasureSpec;        mOldHeightMeasureSpec
 = heightMeasureSpec;        mMeasureCache.put(key,
 ((long)
 mMeasuredWidth) << 32
|                (long)
 mMeasuredHeight & 0xffffffffL); //
 suppress sign extension    }//不过可以看到的是在measure方法里调用了onMeasure方法//所以就能知道
 我们在自定义view的时候一定是重写这个方法! protected
void 
onMeasure(int
widthMeasureSpec, int
heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),
 widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(),
 heightMeasureSpec));    } | 
6.简要分析view的measure流程?
答:先回顾问题4,viewgroup 算出子view的spec以后 会调用子view的measure方法,而子view的measure方法 我们问题5也看过了实际上是调用的onMeasure方法
所以我们只要分析好onMeasure方法即可,注意onMeasure方法的参数 正是他的父view算出来的那2个spec的值(这里view的measure方法会把这个spec里的specSize值做略微的修改 这个部分 不做分析 因为measure方法修改specSize的部分很简单)。
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
 | 
//可以看出来这个就是setMeasuredDimension方法的调用
 这个方法看名字就知道就是确定view的测量宽高的//所以我们分析的重点就是看这个getDefaultSize
 方法 是怎么确定view的测量宽高的 protected
void 
onMeasure(int
widthMeasureSpec, int
heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),
 widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(),
 heightMeasureSpec));    }//这个方法特别简单
 基本可以认为就是近似的返回spec中的specSize,除非你的specMode是UNSPECIFIED//UNSPECIFIED
 这个一般都是系统内部测量才用的到,这种时候返回size 也就是getSuggestedMinimumWidth的返回值 public
static 
int 
getDefaultSize(int
size, int
measureSpec) {        int
result = size;        int
specMode = MeasureSpec.getMode(measureSpec);        int
specSize = MeasureSpec.getSize(measureSpec);        switch
(specMode) {        case
MeasureSpec.UNSPECIFIED:            result
 = size;            break;        case
MeasureSpec.AT_MOST:        case
MeasureSpec.EXACTLY:            result
 = specSize;            break;        }        return
result;}//跟view的背景相关
 这里不多做分析了protected
int 
getSuggestedMinimumWidth() {        return
(mBackground == null)
 ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());    } | 
7.自定义view中 如果onMeasure方法 没有对wrap_content 做处理 会发生什么?为什么?怎么解决?
答:如果没有对wrap_content做处理 ,那即使你在xml里设置为wrap_content.其效果也和match_parent相同。看问题4的分析。我们可以知道view自己的layout为wrap,那mode就是at_most(不管父亲view是什么specmode).
这种模式下宽高就是等于specSize(getDefaultSize函数分析可知),而这里的specSize显然就是parentSize的大小。也就是父容器剩余的大小。那不就和我们直接设置成match_parent是一样的效果了么?
解决方式就是在onMeasure里 针对wrap 来做特殊处理 比如指定一个默认的宽高,当发现是wrap_content 就设置这个默认宽高即可。
8.ViewGroup有onMeasure方法吗?为什么?
答:没有,这个方法是交给子类自己实现的。不同的viewgroup子类 肯定布局都不一样,那onMeasure索性就全部交给他们自己实现好了。
9.为什么在activity的生命周期里无法获得测量宽高?有什么方法可以解决这个问题吗?
答:因为measure的过程和activity的生命周期 没有任何关系。你无法确定在哪个生命周期执行完毕以后 view的measure过程一定走完。可以尝试如下几种方法 获取view的测量宽高。
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
 | 
//重写activity的这个方法public
void 
onWindowFocusChanged(boolean
hasFocus) {        super.onWindowFocusChanged(hasFocus);        if
(hasFocus) {            int
width = tv.getMeasuredWidth();            int
height = tv.getMeasuredHeight();            Log.v("burning", "width=="
+ width);            Log.v("burning", "height=="
+ height);        }    } | 
或者重写这个方法
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
 | 
@Override    protected
void 
onStart() {        super.onStart();        tv.post(new
Runnable() {            @Override            public
void 
run() {                int
width = tv.getMeasuredWidth();                int
height = tv.getMeasuredHeight();            }        });    } | 
再或者:
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
 | 
@Override    protected
void 
onStart() {        super.onStart();        ViewTreeObserver
 observer = tv.getViewTreeObserver();        observer.addOnGlobalLayoutListener(new
ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public
void 
onGlobalLayout() {                int
width = tv.getMeasuredWidth();                int
height = tv.getMeasuredHeight();                tv.getViewTreeObserver().removeOnGlobalLayoutListener(this);            }        });    } | 
10.layout和onLayout方法有什么区别?
答:layout是确定本身view的位置 而onLayout是确定所有子元素的位置。layout里面 就是通过serFrame方法设设定本身view的 四个顶点的位置。这4个位置以确定 自己view的位置就固定了
然后就调用onLayout来确定子元素的位置。view和viewgroup的onlayout方法都没有写。都留给我们自己给子元素布局
11.draw方法 大概有几个步骤?
答: 一共是4个步骤, 绘制背景———绘制自己——–绘制chrildren—-绘制装饰。
12.setWillNotDraw方法有什么用?
答:这个方法在view里。
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
 | 
/**     *
 If this view doesn‘t do any drawing on its own, set this flag to     *
 allow further optimizations. By default, this flag is not set on     *
 View, but could be set on some View subclasses such as ViewGroup.     *     *
 Typically, if you override {@link #onDraw(android.graphics.Canvas)}     *
 you should clear this flag.     *     *
 @param willNotDraw whether or not this View draw on its own     */    public
void 
setWillNotDraw(boolean
willNotDraw) {        setFlags(willNotDraw
 ? WILL_NOT_DRAW : 0,
 DRAW_MASK);    } | 
用于设置标志位的 也就是说 如果你的自定义view 不需要draw的话,就可以设置这个方法为true。这样系统知道你这个view 不需要draw 可以优化执行速度。viewgroup 一般都默认设置这个为true,因为viewgroup多数都是只负责布局
不负责draw的。而view 这个标志位 默认一般都是关闭的。
13.自定义view 有哪些需要注意的点?
答:主要是要处理wrap_content 和padding。否则xml 那边设置这2个属性就根本没用了。还有不要在view中使用handler 因为人家已经提供了post方法。如果是继承自viewGroup,那在onMeasure和onLayout里面 也要考虑
padding和layout的影响。也就是说specSize 要算一下 。最后就是如果view的动画或者线程需要停止,可以考虑在onDetachedFromWindow里面来做。
针对上述的几点,给出几个简单的自定义view 供大家理解。
给出一个圆形的view 范例:
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
 | 
package
com.example.administrator.motioneventtest;import
android.content.Context;import
android.graphics.Canvas;import
android.graphics.Color;import
android.graphics.Paint;import
android.util.AttributeSet;import
android.view.View;/** *
 Created by Administrator on 2016/2/4. */public
class 
CircleView extends
View {    private
int 
mColor = Color.RED;    private
Paint mPaint = new
Paint(Paint.ANTI_ALIAS_FLAG);    private
void 
init() {        mPaint.setColor(mColor);    }    @Override    protected
void 
onMeasure(int
widthMeasureSpec, int
heightMeasureSpec) {        super.onMeasure(widthMeasureSpec,
 heightMeasureSpec);        int
widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int
widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int
heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        int
heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        //处理为wrap_content时的情况        if
(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {            setMeasuredDimension(200, 200);        } else
if 
(widthSpecMode == MeasureSpec.AT_MOST) {            setMeasuredDimension(200,
 heightSpecSize);        } else
if 
(heightSpecMode == MeasureSpec.AT_MOST) {            setMeasuredDimension(widthSpecSize, 200);        }    }    @Override    protected
void 
onDraw(Canvas canvas) {        super.onDraw(canvas);        //处理padding的情况        final
int 
paddingLeft = getPaddingLeft();        final
int 
paddingRight = getPaddingRight();        final
int 
paddingTop = getPaddingTop();        final
int 
paddingBottom = getPaddingBottom();        int
width = getWidth() - paddingLeft - paddingRight;        int
height = getHeight() - paddingTop - paddingBottom;        int
radius = Math.min(width, height) / 2;        canvas.drawCircle(paddingLeft
 + width / 2,
 paddingTop + height / 2,
 radius, mPaint);    }    public
CircleView(Context context, AttributeSet attrs, int
defStyleAttr) {        super(context,
 attrs, defStyleAttr);        init();    }    public
CircleView(Context context) {        super(context);        init();    }    public
CircleView(Context context, AttributeSet attrs) {        super(context,
 attrs);        init();    }} | 
然后下面再给出一个范例,稍微复杂一点是自定义viewgroup了(主要是加强对onMeasure和onLayout的理解), 需求如下:
一个水平的viewgroup,内部的子元素 为了简单 我们假定他们的宽高都是一样的。来写一个这样的简单的viewgroup。
| 
 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
 | 
package
com.example.administrator.motioneventtest;import
android.content.Context;import
android.util.AttributeSet;import
android.util.Log;import
android.view.View;import
android.view.ViewGroup;/** *
 Created by Administrator on 2016/2/4. *///这里我们只处理了padding的状态
 没有处理margin的状态,子view的margin 对measure和layout的影响//就留给读者自己完成了public
class 
CustomHorizontalLayout extends
ViewGroup {    //设置默认的控件最小是多少
 这里不提供自定义属性了 写死在代码里 你们可以自行拓展    final
int 
minHeight = 0;    final
int 
minWidth = 0;    public
CustomHorizontalLayout(Context context) {        super(context);    }    public
CustomHorizontalLayout(Context context, AttributeSet attrs) {        super(context,
 attrs);    }    public
CustomHorizontalLayout(Context context, AttributeSet attrs, int
defStyleAttr) {        super(context,
 attrs, defStyleAttr);    }    @Override    protected
void 
onMeasure(int
widthMeasureSpec, int
heightMeasureSpec) {        super.onMeasure(widthMeasureSpec,
 heightMeasureSpec);        int
measureWidth = 0;        int
measureHeight = 0;        final
int 
childCount = getChildCount();        measureChildren(widthMeasureSpec,
 heightMeasureSpec);        int
widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        int
widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);        int
heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        int
heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);        final
View childView = getChildAt(0);        final
int 
paddingLeft = getPaddingLeft();        final
int 
paddingRight = getPaddingRight();        final
int 
paddingTop = getPaddingTop();        final
int 
paddingBottom = getPaddingBottom();        //没有子控件
 时 我们的宽高要作特殊处理        if
(childCount == 0)
 {            //当没有子控件时,如果长宽有一个为wrap
 那么就让这个控件以最小的形式展现            //这里我们最小设置为0            if
(widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {                setMeasuredDimension(minWidth,
 minHeight);            } else
{                //否则根据我们的layout属性来                setMeasuredDimension(getLayoutParams().width,
 getLayoutParams().height);            }        } else
if 
(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {            measureWidth
 = childView.getMeasuredWidth() * childCount;            measureHeight
 = childView.getMeasuredHeight();            setMeasuredDimension(paddingLeft
 + measureWidth + paddingRight, paddingTop + measureHeight + paddingBottom);        } else
if 
(heightSpecMode == MeasureSpec.AT_MOST) {            measureHeight
 = childView.getMeasuredHeight();            setMeasuredDimension(paddingLeft
 + paddingRight + widthSpecSize, paddingTop + paddingBottom + measureHeight);        } else
if 
(widthSpecMode == MeasureSpec.AT_MOST) {            measureWidth
 = childView.getMeasuredWidth() * childCount;            setMeasuredDimension(paddingLeft
 + paddingRight + measureWidth, paddingTop + paddingBottom + heightSpecSize);        }    }    @Override    protected
void 
onLayout(boolean
changed, int
l, int
t, int
r, int
b) {        final
int 
paddingLeft = getPaddingLeft();        final
int 
paddingRight = getPaddingRight();        final
int 
paddingTop = getPaddingTop();        final
int 
paddingBottom = getPaddingBottom();        //左边初始位置为0        int
childLeft = 0
+ paddingLeft;        final
int 
childCount = getChildCount();        for
(int
i = 0;
 i < childCount; i++) {            final
View childView = getChildAt(i);            if
(childView.getVisibility() != View.GONE) {                final
int 
childWidth = childView.getMeasuredWidth();                childView.layout(childLeft, 0
+ paddingTop, childLeft + childWidth, paddingTop + childView.getMeasuredHeight());                childLeft
 += childWidth;            }        }    }} | 
[Android][转]Android View绘制13问13答
原文:http://blog.csdn.net/andrewyls/article/details/50978594