Android Custom View: MeasureSpec the true sense of the size of the View control

Custom View is the most common Android development needs, flexible control View size is the first problem faced by developers, for example, why obviously using the same WRAP_CONTENT but with MATCH_PARENT performance. In dealing View size, we all know that the best set up custom sizes View onMeasure, then exactly how a reasonable choice of this size it. Intuitively, there may be need to consider the following questions:

  • Custom View should not exceed the size of the parent control, so as to ensure that they can be displayed in full parent control
  • Customize the View (if it is ViewGroup) child controls should not exceed their size, so as to ensure the child controls Show Full
  • If you explicitly specify the size of View, the best according to the dimensions specified settings

These three issues may be a problem custom ViewGroup most need to consider, first of all to solve the first problem.

Limitations and MeasureSpec parent container

Presuppose, parent container is 300dp * 300dp size, if the sub-View is the layout parameters

<!--场景1-->
android:layout_width="match_parent"
android:layout_height="match_parent"

Well, according to our expectations, hopes child View size and if 300dp * 300dp, if the parameter is the layout of the sub-View

<!--场景2-->
android:layout_width="100dp"
android:layout_height="100dp"

According to our expectations, hopes child View size and if 100dp * 100dp, if the parameter is the layout of the sub-View

<!--场景3-->
android:layout_width="wrap_content"
android:layout_height="wrap_content"

According to our expectations, we hope child View size can be determined according to the size of their needs, but preferably not more than 300dp * 300dp.

So the parent container how to tell the child View these requirements it? MeasureSpec in fact assume this role: MeasureSpec is a parent control parameters provided to the child View as setting the size of the reference itself, only a reference to how much, or View make decisions by themselves . Look under MeasureSpec constituted, MeasureSpec mode and a size composition, mode includes three, UNSPECIFIED, EXACTLY, AT_MOST, size mode is given with reference to the size, particularly the following meanings:

  • UNSPECIFIED (not specified), the sub-control parent control without any restraint, child elements can be any size you want, this MeasureSpec is generally determined by the characteristics of their parent control. For example ScrollView, its sub-View can freely set the size, no matter how high, can scroll through, this time, size in general is pointless.
  • EXACTLY the (complete), designated as the parent control the exact size of the sub-View, View exactly as you would want the child of a given size to handle, with the above scenario 2 with a similar comparison, when the parent control is generally MeasureSpec accordance with its own sub MeasureSpec View layout parameters determined. In this case the general size> 0, there is a determination value.
  • AT_MOST (at most), designated as the parent control sub-element maximum reference size, want the child View size not to exceed this size, with the above three scenarios is quite similar. This model is also the parent control according to their own MeasureSpec with child View layout parameters to determine, usually child View layout parameters using wrap_content time.

ViewGroup first look at the source code measureChild how to construct MeasureSpec child View of:

 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);
 }

View all support for any Padding parameters set in reference to the size of the sub-View, the need to put its own Padding to remove, which is also laying the groundwork for Layout. Then see how getChildMeasureSpec get MeasureSpec passed to the child 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);
}

You can see the parent control will make reference to its own layout parameters MeasureSpec with child View to construct appropriate for the child MeasureSpec View, theft online is a diagram to describe

When the child View received MeasureSpec parent control delivery, you can know how they want to display the parent control, the point for developers is onMeasure function, first look at the realization View.java in onMeasure function:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

BACKGROUND wherein getSuggestedMinimumWidth is provided with a minimum size to obtain a reference dimension according to alternate, then look getDefaultSize, as follows:

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;
}

You can see, if not custom rewriting onMeasure View function, MeasureSpec.AT_MOST with MeasureSpec.AT_MOST performance is the same, that is, to the scene 2 with 3 performance is actually the same, that is, wrap_content match_parent just one effect, now we know that the main role of MeasureSpec: parent to child View transfer control reference , then after the child View how to get it?

Custom View size determination

After receiving MeasureSpec parent control passed, View how to handle his size it? View onMeasure is the most reasonable time to measure the size, if not ViewGroup View on relatively simple, only need to refer to MeasureSpec, with their needs and size can be set, the default onMeasure is completely passed MeasureSpec set their own according to the size of the parent control of. The focus here talk about ViewGroup, in order to obtain a reasonable width and height dimensions, ViewGroup in the calculation of their own size, you must know in advance all child View dimensions, for example, with a common flow layout FlowLayout to explain how a reasonable set given their size.

First analyze the characteristics of FLowLayout flow layout (left to right) are: FLowLayout child View all placed in order from left to right, if the current row, relax on the wrap. From the point of view of the layout of the loss characteristics in determining FLowLayout size, we need to know the following information,

  • FlowLayout passed to the parent container of MeasureSpec recommended size (beyond, does not show up, not any sense)
  • FlowLayout View all sub-width and width: height with the width calculation time required to use.
  • Comprehensive MeasureSpec with their needs, come to a reasonable size

First, look at the transfer of the parent container to MeasureSpec FlowLayout, for developers, it can be found in onMeasure function is passed in the parameter onMeasure, its meaning has been said above, now, how to use more reasonable? In fact ViewGroup.java source also offers a relatively simple method, there are two commonly used measureChildren with resolveSize, in the previous analysis, we know measureChildren calls getChildMeasureSpec create MeasureSpec sub-View, and measure the size of each sub-View by measureChild . So resolveSize it, look at the source code below, resolveSize (int size, int measureSpec) of the two input parameters, the first parameter: size, is the View itself is going to get the size of the second parameter: measureSpec, in fact, the parent control is passed to the View, View the recommended acquisition of size, resolveSize is two parameters into consideration, the final recommendations to a size:

 public static int resolveSize(int size, int measureSpec) {
        return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK;
    }

public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
    final int specMode = MeasureSpec.getMode(measureSpec);
    final int specSize = MeasureSpec.getSize(measureSpec);
    final int result;
    switch (specMode) {
        case MeasureSpec.AT_MOST:
            if (specSize < size) {
                result = specSize | MEASURED_STATE_TOO_SMALL;
            } else {
                result = size;
            }
            break;
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
   case MeasureSpec.UNSPECIFIED:
        default:
            result = size;
    }
    return result | (childMeasuredState & MEASURED_STATE_MASK);
}

can be seen:

  • If the parent control is passed to MeasureSpec the mode is MeasureSpec.UNSPECIFIED, it means, there is no restriction on the parent control yourself, then choose size size size they need
  • MeasureSpec mode if the parent control is passed to MeasureSpec.EXACTLY, it shows a clear parent control requirements, want to use the size measureSpec in, then it is recommended to use MeasureSpec.getSize (measureSpec)
  • MeasureSpec mode if the parent control is passed to MeasureSpec.AT_MOST, it means that the parent control myself not to hope beyond MeasureSpec.getSize (measureSpec), if exceeded, would choose MeasureSpec.getSize (measureSpec), or with their own desired size on the line the

For the FlowLayout, assume that each sub-View can be filled with the FlowLayout can, therefore, be measured directly measureChildren all child View sizes:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int paddingLeft = getPaddingLeft();
    int paddingRight = getPaddingRight();
    int paddingBottom = getPaddingBottom();
    int paddingTop = getPaddingTop();
    int count = getChildCount();
    int maxWidth = 0;
    int totalHeight = 0;
    int lineWidth = 0;
    int lineHeight = 0;
    int extraWidth = widthSize - paddingLeft - paddingRight;

    <!--直接用measureChildren测量所有的子View的高度-->
    measureChildren(widthMeasureSpec, heightMeasureSpec);

    <!--现在可以获得所有子View的尺寸-->

    for (int i = 0; i < count; i++) {
        View view = getChildAt(i);
        if (view != null && view.getVisibility() != GONE) {
            if (lineWidth + view.getMeasuredWidth() > extraWidth) {
                totalHeight += lineHeight ;
                lineWidth = view.getMeasuredWidth();
                lineHeight = view.getMeasuredHeight();
                maxWidth = widthSize;
            } else {
                lineWidth += view.getMeasuredWidth();
            }
            <!--获取每行的最高View尺寸-->
            lineHeight = Math.max(lineHeight, view.getMeasuredHeight());
        }
    }
    totalHeight = Math.max(totalHeight + lineHeight, lineHeight);
    maxWidth = Math.max(lineWidth, maxWidth);

    <!--totalHeight 跟 maxWidth都是FlowLayout渴望得到的尺寸-->
    <!--至于合不合适,通过resolveSize再来判断一遍,当然,如果你非要按照自己的尺寸来,也可以设定,但是不太合理-->
    totalHeight = resolveSize(totalHeight + paddingBottom + paddingTop, heightMeasureSpec);
    lineWidth = resolveSize(maxWidth + paddingLeft + paddingRight, widthMeasureSpec);
    setMeasuredDimension(lineWidth, totalHeight);
}

It can be seen sized custom ViewGroup really only need three:

  • Measurement of all sub-View, the View size obtain all sub
  • Desired size calculated according to their characteristics
  • MeasureSpec into consideration the size of the required transmission with parent control, arrive at a reasonable size

View the top MeasureSpec who is designated

MeasureSpec passed to the child View is the parent container determined according to their MeasureSpec and child View layout parameters, then the root MeasureSpec who created it? We use the two most commonly used to explain Window, Activity and Dialog, DecorView is the root layout Activity, passing to MeasureSpec DecorView system is determined in accordance Activity or Dialog's Theme, that is to say, according to the first direct MeasureSpec Window construction properties, generally for Activity, the root is EXACTLY + MeasureSpec screen size, for Dialog, if no special setting screen sizes will be used AT_MOST +. Here involves WindowManagerService with ActivityManagerService, interested can track what WindowManager.LayoutParams, later also specifically analyze, for example, to achieve full-screen Dialog most simple test of just those related knowledge.

It is engaged in the development of the Android engineers for seven years, a lot of people ask me privately, 2019 Android how advanced the science, there is no method?

Yes, at the beginning I spent more than a month to sort out learning materials, hoping to help those who want to enhance the advanced Android development, but do not know how advanced learning friends. [ Including advanced UI, performance optimization, Architect courses, NDK, Kotlin, hybrid development (ReactNative + Weex), Flutter and other technical information architecture ], hoping to help review your pre-interview and find a good job, but also save in time they search for information online to learn.

Obtaining: Add Android architecture exchange QQ group chat: 513 088 520, into the group that is to receive the information! ! !

Click on the link to join a group chat Android mobile architecture [total population]: join a group chat

Sourcebook

Guess you like

Origin blog.csdn.net/weixin_45136073/article/details/92991042