View.measure()
、ViewTreeObserver
监听事件、重写onSizeChanged()
和onLayout()
方法等。使用ViewTreeObserver.OnGlobalLayoutListener
可以在视图绘制完成后获取其宽高。在Android开发中,获取View组件的宽度和高度是一项常见的需求,由于View的绘制过程涉及到多个阶段,如测量(measure)、布局(layout)和绘制(draw),直接调用getWidth()或getHeight()方法在某些情况下可能无法得到预期的结果,本文将详细介绍几种有效的方法来获取View在布局中的实际宽度和高度,并提供相应的代码示例和表格归纳。
一、使用View.measure方法
这种方法通过手动测量View来获取其宽度和高度,不过,这种方法得到的尺寸可能与最终绘制时的尺寸不一致,因为它没有考虑父布局的影响。
示例代码:
int width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); int height = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); view.measure(width, height); int measuredWidth = view.getMeasuredWidth(); // 获取宽度 int measuredHeight = view.getMeasuredHeight(); // 获取高度
二、使用ViewTreeObserver.OnPreDrawListener监听事件
在视图将要绘制时调用该监听事件,可以获取到视图即将绘制时的宽度和高度,需要注意的是,这个监听器会被多次调用,因此获取到尺寸后需要移除监听器。
示例代码:
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { view.getViewTreeObserver().removeOnPreDrawListener(this); int width = view.getWidth(); // 获取宽度 int height = view.getHeight(); // 获取高度 return true; } });
三、使用ViewTreeObserver.OnGlobalLayoutListener监听事件
当布局发生改变或者某个视图的可视状态发生改变时,会调用该事件,这是最常用的方法之一,因为它确保了在视图布局完成后获取尺寸。
示例代码:
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (Build.VERSION.SDK_INT >= 16) { view.getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { view.getViewTreeObserver().removeGlobalOnLayoutListener(this); } int width = view.getWidth(); // 获取宽度 int height = view.getHeight(); // 获取高度 } });
四、使用View.post方法
Runnable对象中的方法会在View的measure、layout等事件完成后触发,这个方法只会执行一次,且逻辑简单,是推荐使用的方法之一。
示例代码:
view.post(new Runnable() { @Override public void run() { int width = view.getWidth(); // 获取宽度 int height = view.getHeight(); // 获取高度 } });
五、重写View的onSizeChanged方法(不推荐)
虽然可以通过重写onSizeChanged方法来获取视图的大小变化,但由于该方法会被多次调用,且需要继承View类,因此一般不推荐使用。
示例代码:
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); int width = w; // 获取宽度 int height = h; // 获取高度 }
六、重写View的onLayout方法(不推荐)
与onSizeChanged类似,onLayout方法也会被多次调用,且需要继承View类,因此也不推荐使用。
示例代码:
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); int width = r l; // 获取宽度 int height = b t; // 获取高度 }
七、使用View.OnLayoutChangeListener监听事件(API >= 11)
在视图的layout改变时调用该事件,但同样需要移除监听器以避免多次调用。
示例代码:
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { view.removeOnLayoutChangeListener(this); int width = right left; // 获取宽度 int height = bottom top; // 获取高度 } });
以下是各方法的比较表格:
方法 | 适用场景 | 优点 | 缺点 | 示例代码位置 |
View.measure | 手动测量View | 简单直接 | 不考虑父布局影响 | [方法一](#) |
ViewTreeObserver.OnPreDrawListener | 视图将要绘制时 | 可获取即将绘制时的尺寸 | 需移除监听器 | [方法二](#) |
ViewTreeObserver.OnGlobalLayoutListener | 布局或视图状态改变时 | 确保布局完成后获取尺寸 | 需移除监听器 | [方法三](#) |
View.post | View事件完成后 | 逻辑简单,只执行一次 | 无 | [方法四](#) |
重写onSizeChanged | 视图大小变化时 | 不推荐,多次调用 | 需继承View类 | [方法五](#) |
重写onLayout | 视图布局时 | 不推荐,多次调用 | 需继承View类 | [方法六](#) |
View.OnLayoutChangeListener | 视图layout改变时(API >= 11) | 需移除监听器 | 无 | [方法七](#) |
推荐使用ViewTreeObserver.OnGlobalLayoutListener或View.post方法来获取View在布局中的实际宽度和高度,因为它们既简单又有效。