美文网首页
Android中View绘制知识点,父视图高为match_par

Android中View绘制知识点,父视图高为match_par

作者: IT枫 | 来源:发表于2019-07-22 14:22 被阅读0次

新浪乐居有道面试题"如果父视图的高设置为wrap_content,它的子View高也是wrap_content,那么子View的高度如何显示?"这种问题就是考察你对源码的熟悉度。

我们直接查看View工作原理源码,梳理出三个方法如下,都是View类中的方法。

onMeasure()方法

getSuggestMinimumWidth()方法

getDefaultSize()方法

onMeasure()方法

测量方法onMeasure()接受父视图施加的宽高"测量规格"。


//widthMeasureSpec父类的宽度测量准则

//heightMeasureSpec父类的高度测量准则

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  {

    int  widthSize = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);

    int heightSize = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);

    setMeasureDimension(widthSize, heightSize);

}

getSuggestedMinimumWidth()方法
该方法在父容器对子View宽度没有任何限制的情况下才会有作用,一般用于系统的内部测量,在此我们不做研究。

getDefaultSize()方法

这里就是考察的重点,一般情况下子view的高度由父视图的"测量规格来决定"。


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.exactly:

        case MeasureSpec.at_most: {

            result = specSize;

        }

        break;

    }

    return result;

}

得出结论:无论子View高度为match_parent、wrap_content或者具体的高度值(如100px)测量的高度都是父容器的高度。

直接继承View的自定义控件需要重写onMeasure()方法并设置wrap_content时的自身大小,否则在布局中使用wrap_content就相当于使用match_parent。指定的默认的内部宽/高(mWidth和mHeight),这个没有固定的依据,根据需要灵活指定即可。如果查看TextView、ImageView等的源码就可以知道,针对wrap_content情形,它们的onMeasure()方法均做了特殊处理。

以上就是本篇文章所要讲述的问题了,以下的源码仅供喜欢钻研的读者阅读。

以下为TextView的onMeasure()方法源码

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width;
        int height;

        BoringLayout.Metrics boring = UNKNOWN_BORING;
        BoringLayout.Metrics hintBoring = UNKNOWN_BORING;

        if (mTextDir == null) {
            mTextDir = getTextDirectionHeuristic();
        }

        int des = -1;
        boolean fromexisting = false;
        final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
                ?  (float) widthSize : Float.MAX_VALUE;

        if (widthMode == MeasureSpec.EXACTLY) {
            // Parent has told us how big to be. So be it.
            width = widthSize;
        } else {
            if (mLayout != null && mEllipsize == null) {
                des = desired(mLayout);
            }

            if (des < 0) {
                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
                if (boring != null) {
                    mBoring = boring;
                }
            } else {
                fromexisting = true;
            }

            if (boring == null || boring == UNKNOWN_BORING) {
                if (des < 0) {
                    des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
                            mTransformed.length(), mTextPaint, mTextDir, widthLimit));
                }
                width = des;
            } else {
                width = boring.width;
            }

            final Drawables dr = mDrawables;
            if (dr != null) {
                width = Math.max(width, dr.mDrawableWidthTop);
                width = Math.max(width, dr.mDrawableWidthBottom);
            }

            if (mHint != null) {
                int hintDes = -1;
                int hintWidth;

                if (mHintLayout != null && mEllipsize == null) {
                    hintDes = desired(mHintLayout);
                }

                if (hintDes < 0) {
                    hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring);
                    if (hintBoring != null) {
                        mHintBoring = hintBoring;
                    }
                }

                if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
                    if (hintDes < 0) {
                        hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
                                mHint.length(), mTextPaint, mTextDir, widthLimit));
                    }
                    hintWidth = hintDes;
                } else {
                    hintWidth = hintBoring.width;
                }

                if (hintWidth > width) {
                    width = hintWidth;
                }
            }

            width += getCompoundPaddingLeft() + getCompoundPaddingRight();

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

            if (widthMode == MeasureSpec.AT_MOST) {
                width = Math.min(widthSize, width);
            }
        }

        int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
        int unpaddedWidth = want;

        if (mHorizontallyScrolling) want = VERY_WIDE;

        int hintWant = want;
        int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();

        if (mLayout == null) {
            makeNewLayout(want, hintWant, boring, hintBoring,
                          width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
        } else {
            final boolean layoutChanged = (mLayout.getWidth() != want) || (hintWidth != hintWant)
                    || (mLayout.getEllipsizedWidth()
                            != width - getCompoundPaddingLeft() - getCompoundPaddingRight());

            final boolean widthChanged = (mHint == null) && (mEllipsize == null)
                    && (want > mLayout.getWidth())
                    && (mLayout instanceof BoringLayout
                            || (fromexisting && des >= 0 && des <= want));

            final boolean maximumChanged = (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum);

            if (layoutChanged || maximumChanged) {
                if (!maximumChanged && widthChanged) {
                    mLayout.increaseWidthTo(want);
                } else {
                    makeNewLayout(want, hintWant, boring, hintBoring,
                            width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
                }
            } else {
                // Nothing has changed
            }
        }

        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;

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

        int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
        if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
            unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
        }

        /*
         * We didn't let makeNewLayout() register to bring the cursor into view,
         * so do it here if there is any possibility that it is needed.
         */
        if (mMovement != null
                || mLayout.getWidth() > unpaddedWidth
                || mLayout.getHeight() > unpaddedHeight) {
            registerForPreDraw();
        } else {
            scrollTo(0, 0);
        }

        setMeasuredDimension(width, height);
    }

相关文章

网友评论

      本文标题:Android中View绘制知识点,父视图高为match_par

      本文链接:https://www.haomeiwen.com/subject/ernllctx.html