getMeasuredWidth()和getWidth()

如何获取view的宽/高度?许多人都会用getWidth()或者getMeasuredWidth(),但是这两者有什么不一样呢?

View的绘制过程是measure->layout->draw。看下measure的代码:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

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.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

看下getDefaultSize函数,这个函数测量到的大小是根据父容器的measureSpec同时结合view本身的layoutparams来确定子元素的measureSpec。这时计算出的大小是getMeasuredWidth(),但是view最终的大小是在layout阶段确定的(用getWidth()读取),所以这里要加以区分,但是几乎所有情况下view的测量和最终大小是相等的。所以一个比较好的习惯是在onLayout函数中去获取view的大小。

如果在一个activity启动的时候,想要去获取某个view的宽高,是不可以在onCreate或者onResume等生命周期函数中去取,这是因为view的绘制和activity的生命周期是异步的,所以在这些生命周期函数中去绘制的话,会导致获取到的宽度为0。下面介绍几种方法来获取:

  1. 通过onWindowFocusChanged,这个方法的含义是:view已经初始化完毕了,宽高已经准备好了,这个时候去获取宽高是没问题的。当Activity的窗口获取焦点或失去焦点均会被调用,太频繁,这样不好。
  2. view.post(runnable):通过post可以将一个runnable投递到消息队列的尾部,然后等looper调用此runnable的时候,view也已经初始化好了。典型代码:
protected void onStart(){
    super.onStart();
    view.post(new Runnable(){
         @Override
         public void run(){
             int width = view.getWidth();
             int height = view.getHeight();
         }
     });
}
  1. ViewTreeObserver:使用ViewTreeObserver的众多回调可以完成这个功能,比如使用onGlobalLayoutListener这个接口,当view树的状体发生改变或view树内部的view的可见性发生改变时,onGlobalLayout方法将被回调,因此这是一个获取view宽高很好的机会。需要注意的是,随着view树的变化,该回调将会被调用多次,获取宽高后需要remove该Listener。
void func(final View view){
    view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            int width = view.getWidth();
            int height = view.getHeight();
        }
    });
}

MeasureSpec.UNSPECIFIED这种情况,一般用于系统内部的测量过程,getDefaultSize传入的specSize分别为getSuggestedMinimumWidth()和getSuggestedMinimumHeight()。

protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }

protected int getSuggestedMinimumHeight() {
        return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());

    }

如果这个view没有设置背景,那么返回的是mMinWidth或者mMinHeight,这两个值对应的是android:minWidth和android:minHeight,但是如果设置了background,就取background.getMinimumHeight和mMinHeight的大值。
那Drawable的getMinimumHeight()函数返回的是什么呢?

public int getMinimumHeight() {
        final int intrinsicHeight = getIntrinsicHeight();
        return intrinsicHeight > 0 ? intrinsicHeight : 0;
}

可以看出getMinimumHeight返回的就是drawable的原始高度。如果是BitmapDrawable的话,返回bitmap的原始高度,如果是ShapeDrawable无原始高度,返回0。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值