Android View measure (三) 经常用法
ViewGroup.measureChildren()
ViewGroup.measureChild()
ViewGroup.measureChildWithMargins()
ViewGroup.measureChild()
ViewGroup.measureChildWithMargins()
/** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding * and margins. The child must have MarginLayoutParams The heavy lifting is * done in getChildMeasureSpec. * * @param child The child to measure * @param parentWidthMeasureSpec The width requirements for this view * @param widthUsed Extra space that has been used up by the parent * horizontally (possibly by other children of the parent) * @param parentHeightMeasureSpec The height requirements for this view * @param heightUsed Extra space that has been used up by the parent * vertically (possibly by other children of the parent) */ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { // 1 类型转换 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); // 2 getChildMeasureSpec方法 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); }
此方法针对第一个參数传递的子视图进行Measure操作。
1. 传递的子视图LayoutParams必须是或者继承自MarginLayoutParams,否者会抛出转换异常
/** * Does the hard part of measureChildren: figuring out the MeasureSpec to * pass to a particular child. This method figures out the right MeasureSpec * for one dimension (height or width) of one child view. * * The goal is to combine information from our MeasureSpec with the * LayoutParams of the child to get the best possible results. For example, * if the this view knows its size (because its MeasureSpec has a mode of * EXACTLY), and the child has indicated in its LayoutParams that it wants * to be the same size as the parent, the parent should ask the child to * layout given an exact size. * * @param spec The requirements for this view * @param padding The padding of this view for the current dimension and * margins, if applicable * @param childDimension How big the child wants to be in the current * dimension * @return a MeasureSpec integer for the child */ 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.FILL_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.FILL_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.FILL_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { // 1 .... // 2 getChildMeasureSpec方法 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); }
调用此方法处。看看这三个參数的详细含义。
1. spec : 此视图要求的规格 【parentWidthMeasureSpec】
2. padding : 当前ViewGroup视图的padding值与传入子视图的margin值 【mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed】?widthUsed
3. childDimension : 传入參数的视图,期望展示多大【lp.width】
** 举一个样例
扩展:MeasureSpec方法与拼接方式
/** * Utility to reconcile a desired size with constraints imposed by a MeasureSpec. * Will take the desired size, unless a different size is imposed by the constraints. * * @param size How big the view wants to be * @param measureSpec Constraints imposed by the parent * @return The size this view should be. */ public static int resolveSize(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: result = Math.min(size, specSize); break; case MeasureSpec.EXACTLY: result = specSize; break; } return result; }
/** * Creates a measure specification based on the supplied size and mode. * * The mode must always be one of the following: * <ul> * <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li> * <li>{@link android.view.View.MeasureSpec#EXACTLY}</li> * <li>{@link android.view.View.MeasureSpec#AT_MOST}</li> * </ul> * * @param size the size of the measure specification * @param mode the mode of the measure specification * @return the measure specification based on size and mode */ public static int makeMeasureSpec(int size, int mode) { return size + mode; }
/** * <p>This mehod must be called by {@link #onMeasure(int, int)} to store the * measured width and measured height. Failing to do so will trigger an * exception at measurement time.</p> * * @param measuredWidth the measured width of this view * @param measuredHeight the measured height of this view */ protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { mMeasuredWidth = measuredWidth; mMeasuredHeight = measuredHeight; mPrivateFlags |= MEASURED_DIMENSION_SET; } /** * The width of this view as measured in the most recent call to measure(). * This should be used during measurement and layout calculations only. Use * {@link #getWidth()} to see how wide a view is after layout. * * @return The measured width of this view. */ public final int getMeasuredWidth() { return mMeasuredWidth; } /** * The height of this view as measured in the most recent call to measure(). * This should be used during measurement and layout calculations only. Use * {@link #getHeight()} to see how tall a view is after layout. * * @return The measured height of this view. */ public final int getMeasuredHeight() { return mMeasuredHeight; }