Android 中获取 View 宽高的几种方式,以及它们的适用场景和注意事项:
1. View.getWidth()
和 View.getHeight()
-
原理: 直接从 View 对象中获取已经计算好的宽度和高度。
-
优点: 简单直接。
-
缺点: 在
onCreate()
、onStart()
等生命周期方法中,View 可能还没有完成测量,此时获取到的值可能是 0。 通常在onResume()
之后才能保证获取到正确的值。
问题本质:
onCreate()
:setContentView()
仅加载布局,未开始测量。onStart()
/onResume()
:界面可见,但测量可能仍未完成(尤其是复杂布局或异步数据加载时)。
-
适用场景: 在 View 已经完成布局之后,需要获取其宽高时使用。例如,在用户交互事件(如点击事件)中,或者在
onWindowFocusChanged(boolean hasFocus)
方法中。 -
代码示例:
@Override protected void onResume() { super.onResume(); int width = myView.getWidth(); int height = myView.getHeight(); Log.d("ViewSize", "Width: " + width + ", Height: " + height); }
2.
View.getMeasuredWidth()
和View.getMeasuredHeight()
-
原理: 获取 View 的测量宽度和测量高度。 View 在布局过程中会经过测量阶段,
getMeasuredWidth()
和getMeasuredHeight()
返回的就是测量阶段计算出的宽高。 -
优点: 比
getWidth()
和getHeight()
更早获取到 View 的宽高信息。 -
缺点: 测量宽高可能与最终显示的宽高不同。 例如,如果 View 的
layout_width
或layout_height
设置为wrap_content
,并且父容器对其进行了限制,那么测量宽高可能只是一个预估值。 此外,如果 View 进行了重新测量,getMeasuredWidth()
和getMeasuredHeight()
的值也会发生变化。 -
适用场景: 在 View 还没有完全布局完成,但需要提前知道其大致宽高信息时使用。例如,在自定义 View 的
onMeasure()
方法中。 -
代码示例:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int measuredWidth = getMeasuredWidth(); int measuredHeight = getMeasuredHeight(); Log.d("ViewSize", "Measured Width: " + measuredWidth + ", Measured Height: " + measuredHeight); }
3.
ViewTreeObserver.OnGlobalLayoutListener
-
ViewTreeObserver.OnGlobalLayoutListener
是 Android 中用于监听 视图树(ViewTree)全局布局变化 的接口,常用于在 View 完成测量和布局后 获取其宽高、位置等属性。 -
优点: 可以保证在 View 已经完成布局之后获取到正确的宽高值。 即使 View 的宽高在布局过程中发生了变化,也能及时获取到最新的值。
-
缺点: 需要注册监听器,并且在获取到宽高后需要移除监听器,否则会造成性能问题。
-
适用场景: 需要在 View 布局完成后立即获取其宽高,并且需要保证获取到的值是准确的。
-
代码示例:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myView = findViewById(R.id.my_view); ViewTreeObserver vto = myView.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { myView.getViewTreeObserver().removeOnGlobalLayoutListener(this); int width = myView.getWidth(); int height = myView.getHeight(); Log.d("ViewSize", "Width: " + width + ", Height: " + height); } }); }
4.
View.post(Runnable)
-
原理: 将一个 Runnable 对象添加到 View 的消息队列中,该 Runnable 对象会在 View 布局完成后执行。
-
优点: 可以保证在 View 已经完成布局之后获取到正确的宽高值,并且代码简洁。
-
缺点: 相对于
OnGlobalLayoutListener
,可能稍微延迟一点执行。 -
适用场景: 需要在 View 布局完成后立即获取其宽高,并且对执行时间要求不高。
-
代码示例:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myView = findViewById(R.id.my_view); myView.post(new Runnable() { @Override public void run() { int width = myView.getWidth(); int height = myView.getHeight(); Log.d("ViewSize", "Width: " + width + ", Height: " + height); } }); }
5.
View.addOnLayoutChangeListener(OnLayoutChangeListener)
(API Level 11+) -
原理: 监听 View 的布局变化。 当 View 的布局发生改变时,
onLayoutChange()
方法会被调用。 -
优点: 可以监听 View 的布局变化,并在布局变化后获取最新的宽高值。
-
缺点: 只在 API Level 11 及以上可用。
-
适用场景: 需要在 View 布局变化后立即获取其宽高,并且需要监听 View 的布局变化。
-
代码示例:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myView = findViewById(R.id.my_view); myView.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) { int width = myView.getWidth(); int height = myView.getHeight(); Log.d("ViewSize", "Width: " + width + ", Height: " + height); } }); }