美文网首页
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