返回

为何 Android 中 View 的 Width 和 Height 初始值为 0?如何解决?

java

View 的 Width 和 Height 初始值为 0 的原因和解决方案

问题

在 Android 开发中,使用 ViewgetWidth()getHeight() 方法获取视图的宽高时,经常会发现刚创建的视图返回 0。这是因为视图需要时间来测量并确定其尺寸。

原因

当我们创建视图时,Android 系统会执行一个布局过程来确定视图的大小和位置。这个过程可能需要一些时间,特别是在视图层级复杂或涉及复杂布局的情况下。在此期间,getWidth()getHeight() 返回 0,因为视图尚未测量完成。

解决方案

要解决这个问题,我们需要使用 ViewTreeObserver 来监听视图的布局变化。ViewTreeObserver 是一个类,它允许我们注册回调以响应视图的布局更改。当视图的布局发生变化时,ViewTreeObserver 将触发 OnGlobalLayoutListener,这使我们能够在视图尺寸确定的情况下获取其宽高。

以下是如何使用 ViewTreeObserver 获取视图宽高的代码示例:

val view = findViewById<View>(R.id.my_view)

view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
        val width = view.width
        val height = view.height

        // 移除监听器,避免重复调用
        view.viewTreeObserver.removeOnGlobalLayoutListener(this)
    }
})

在上面的代码中,我们首先获取要测量宽高的视图。然后,我们使用 viewTreeObserveraddOnGlobalLayoutListener() 方法添加一个监听器。当视图的布局发生变化时,OnGlobalLayoutListeneronGlobalLayout() 方法会被调用。在这个方法中,我们可以获取视图的宽高并执行必要的操作。最后,我们使用 viewTreeObserver.removeOnGlobalLayoutListener() 方法移除监听器,以避免重复调用。

在真实场景中的应用

让我们通过一个实际示例来说明如何使用 ViewTreeObserver 来解决这个问题。假设我们有一个按钮,我们想在创建时对其应用一个旋转动画。但是,如果我们在按钮创建后立即尝试获取其宽高,我们可能会得到 0,因为视图尚未测量完成。

我们可以使用 ViewTreeObserver 延迟获取宽高,直到视图完成测量。以下是修改后的代码:

val button = findViewById<Button>(R.id.my_button)

button.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
        val width = button.width
        val height = button.height

        val rotateAnimation = RotateAnimation(0f, 360f, width / 2f, height / 2f)

        button.startAnimation(rotateAnimation)

        button.viewTreeObserver.removeOnGlobalLayoutListener(this)
    }
})

通过这种方式,我们可以在视图的宽高确定后立即获取其宽高,并使用这些宽高来设置旋转动画。

常见问题解答

1. 为什么 ViewTreeObserveronGlobalLayout() 方法会被多次调用?

onGlobalLayout() 方法在每次布局更改时都会被调用,包括在动画、用户交互或旋转屏幕期间。为了避免重复调用,在 onGlobalLayout() 方法中移除监听器非常重要。

2. 我可以同时为同一视图添加多个 ViewTreeObserver 监听器吗?

是的,你可以为同一个视图添加多个 ViewTreeObserver 监听器。每个监听器都会在布局更改时被调用。

3. ViewTreeObserver 是否与 View 的生命周期相关?

ViewTreeObserverView 的生命周期无关。它会在 View 附加到窗口时自动创建,并在 View 从窗口分离时自动销毁。

4. 我可以使用 ViewTreeObserver 来监听其他类型的布局更改吗?

是的,ViewTreeObserver 可以用来监听各种布局更改,包括 onPreDraw()onDraw()onWindowFocusChanged()

5. ViewTreeObserver 是否会影响性能?

添加 ViewTreeObserver 监听器可能会对性能产生轻微的影响。但是,对于大多数应用程序来说,这种影响通常可以忽略不计。