android view的parent,Android中View绘制知识点,父视图高为match_parent,子视图的高为wrap_content,那么子视图高如何显示?...

新浪乐居有道面试题"如果父视图的高设置为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);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值