关于getHeight与getMeasureHeight的问题

上上一篇讲了一些获取View宽高的一些方式,但这里会有一个问题可能有些人不知道,就是有些地方使用了getHeight,有些地方使用了getMeasureHeight,什么时候getHeight是有效的,什么时候getMeasureHeight是有效的,很多人都不是很清楚,大部分人只知道getMeasureHeight是获取一个View的测量高度,而getHeight是获取View的真实高度,那么问题来了,实时真的是如此吗?先看一个例子。

   <LinearLayout
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="30dp"
    android:background="@color/colorAccent">
    <View
        android:id="@+id/text"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="@color/colorPrimaryDark"/>
</LinearLayout>

父布局30dp高度,而内部的TextView的高度100dp已经超过了父布局的高度,这时候通过上一篇的方式获取View的高度是多少呢。结果如下。

12-22 20:41:57.511 3943-3943/com.example.zhangtao21.measureheight E/measure text1: 0
12-22 20:41:57.634 3943-3943/com.example.zhangtao21.measureheight E/global text1: 100
12-22 20:41:57.686 3943-3943/com.example.zhangtao21.measureheight E/runnable text1: 100
12-22 20:41:57.791 3943-3943/com.example.zhangtao21.measureheight E/onWindowFocusChanged : text1: 100dp

除了直接以measure的方式获取到的高度是0外,其他的高度都是100dp,但是这个View的高度并没有100dp,那么说明getHeight并不一定获取到的是View的真实高度。关于getHeight和getMeasureHeight请记住这么一句话。getMeasureHeight是由自身决定的,getHeight是由他的父布局决定的。先解释一下getMeasureHeight就是自身的测量高度,不考虑父布局高度,我测量自己是多高拿到的getMeasureHeight就是多少,getHeight是父布局告诉我多高,我拿到的就是多少。两种方法获取到的都不是View的真实高度。为什么这么说请看源码。。。

View的getMeasureHeight:

public final int getMeasuredHeight() {
    return mMeasuredHeight & MEASURED_SIZE_MASK;
}

获取到的是View中的mMeasuredHeight的变量。这个变量的赋值是在onMeasure中调用的setMeasuredDimension(width, height),我们平时在自定义View的时候在onMeasure中要让我们计算的宽高生效必须调用的方法。
setMeasuredDimensionRaw

private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
    mMeasuredWidth = measuredWidth;
    mMeasuredHeight = measuredHeight;

    mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}

该方法会对 mMeasuredWidth,mMeasuredHeight进行赋值。setMeasuredDimension是final的,setMeasuredDimensionRaw是private,都不允许我们进行复写。onMeasure是有measure方法
调用。整个视图树的measure的过程如下。

measure.png

而只要一个View调用了measure方法,获取到的getMeasureHeight就不是0,至于准不准确我们只能说呵呵了,在onMeasure中计算的根据不同的Mode获取到的数值会不一样,在什么情况下(Mode)这个View本身会有多高,全部要看onMeasure的实现了,而每一次调用Measure都会对mMeasuredHeight进行赋值,所以每一次measure后获取到的数值就有可能不同。

关于getheight的其实很简单,底部减去顶部。

@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
    return mBottom - mTop;
}

赋值是在View的layout中

@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
 //****
    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
 //****
}

只看这么一行 setOpticalFrame最终也会调用setFrame,在 setFrame中会对mLeft ,mTop ,mRight,mBottom 进行赋值,layout方法是由ViewGroup调用,所以mBottom和mTop最终都是由父布局决定的,父布局分配给子View多少获取到的就是多少。父布局在自己的onLayout中获取到每一个View的MeasureHeight,再依据MeasureHeight分配每一个布局的Height,Measureheight只是告诉父布局,子View想要多少,而最终分配了多少还是由父布局决定,既然我们知道了这个原理,我们写这样的一个ViewGroup,

public class CustomViewGroup extends FrameLayout{
    public CustomViewGroup(Context context) {
        super(context);
    }

    public CustomViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        View view = getChildAt(0);
        view.layout(l,t,r,l+DensityUtil.dp2px(getContext(),300));
    }
}

这个布局不会参考子布局的高度也不会考虑自身的高度,无论申请多少,都会给子View分配定高300dp,这样无论子View真实有多高,测量有多高,调用getHeight获取到的都是300dp,这么说来getheight获取到的并不是准确的,但这里我们的ViewGroup是不符合逻辑,不遵守任何约定的,使用常用的几大布局获取到的getHeight还是比较准确的,至于最开始的问题,想获取到我们可见的View高度,也有办法。

 view.post(new Runnable() {
            @Override
            public void run() {
                int height = 0;
                ViewGroup viewGroup = (ViewGroup) view.getParent();
                if(view.getBottom() > viewGroup.getBottom()){
                     height = viewGroup.getBottom() - view.getTop();
                } else {
                    height = view.getHeight();
                }
                Log.e("realheight"," "+DensityUtil.px2dp(Test1Activity.this, height));
            }
        });

如果子View没有EXACTLY方式超出父布局这是获取到的高度是准确的,如果超出了父布局,这是我们使用父布局的底部减去View的顶部,获取到的就是view的高度。

总结一下
前提正常情况下(无特殊赋值操作):
在onMeasure调用了setMeasuredDimension后获取到的getMeasureHeight就不是0了。
在layout调用后,在onLayout中获取到的getHeight就不是0了,之后也不是0了。

在最后一次onMeasure后,getMeasureHeight获取到的数值就不会在改变了。
在最后一次layout调用后,在onLayout获取到的和之后获取到的getHeight也就不会变了,也就是正常情况下的准确数值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容