(Android)控制項最大最小值失效問題,導致setWidth,setHeight等失效

由viewgroup傳參取得widthMeasureSpec,heightMeasureSpec

主要方法getChildMeasureSpec(parentWidthMeasureSpec,...)

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
/**
     * 遍歷ViewGroup中所有的子控制項,呼叫measuireChild測量寬高
     */
    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
        final int size = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < size; ++i) {
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                //測量某一個子控制項寬高
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
            }
        }
    }

    /**
     * 測量某一個child的寬高
     */
    protected void measureChild(View child, int parentWidthMeasureSpec,
                                int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();
        //取得子控制項的寬高約束規則
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    /**
     * 測量某一個child的寬高,考慮margin值
     */
    protected void measureChildWithMargins(View child,
                                           int parentWidthMeasureSpec, int widthUsed,
                                           int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        //取得子控制項的寬高約束規則
        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);

UNSPECIFIED:不對View大小做限制,如:ListView,ScrollView(可以造成height出現UNSPECIFIED模式)

EXACTLY:確切的大小,如:100dp或者march_parent

AT_MOST:大小不可超過某數值,如:wrap_content

width取值(textview部分原始碼)

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
//EXACTLY模式下
  if (widthMode == MeasureSpec.EXACTLY) {
            // Parent has told us how big to be. So be it.
            width = widthSize;
   }
//其他兩種模式走下面邏輯
   else {
        if (mMaxWidthMode == EMS) {
                width = Math.min(width, mMaxWidth * getLineHeight());
            } else {
                width = Math.min(width, mMaxWidth);
            }

            if (mMinWidthMode == EMS) {
                width = Math.max(width, mMinWidth * getLineHeight());
            } else {
                width = Math.max(width, mMinWidth);
            }

            // Check against our minimum width
            width = Math.max(width, getSuggestedMinimumWidth());
           
//AT_MOST模式
            if (widthMode == MeasureSpec.AT_MOST) {
                width = Math.min(widthSize, width);
            }

}

height取值(textview部分原始碼)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 //EXACTLY模式下
 if (heightMode == MeasureSpec.EXACTLY) {
            // Parent has told us how big to be. So be it.
            height = heightSize;
            mDesiredHeightAtMeasure = -1;
 }
//其他兩種模式走下面邏輯
 else {
            int desired = getDesiredHeight();

            height = desired;
            mDesiredHeightAtMeasure = desired;

//AT_MOST模式
            if (heightMode == MeasureSpec.AT_MOST) {
                height = Math.min(desired, heightSize);
            }
        }

總結,EXACTLY模式下失效,直接取設定大小;AT_MOST模式下失效,取三者(最大值,最小值,確切值)最小;

UNSPECIFIED模式下有效