Android View生命周期:如何在正确时机获取View尺寸

458 阅读2分钟

一、核心原理:View的渲染滞后于Activity生命周期

在 Android 中,View 的尺寸是在其渲染流程的测量(onMeasure布局(onLayout 阶段确定的。而这个渲染流程,发生在 ActivityonCreateonResume 方法之后

  • onCreate 阶段setContentView() 方法只是将布局文件解析成一个视图树,并将其绑定到 ActivityWindow 上。此时,View 的宽高尚未被计算,其值通常为 0。
  • onResume 之后:在 ActivityonResume() 之后,DecorViewActivity 窗口的根视图)才会被添加到 WindowManager 中,并触发 ViewRootImplperformTraversals() 方法,从而开始完整的 measurelayoutdraw 流程。

二、正确获取View尺寸的四种方法

为了在 View 尺寸确定后执行操作,开发者需要使用适当的回调机制。

1. View.post():通用且可靠

  • 原理View.post() 将一个 Runnable 放入主线程的消息队列的队尾。由于 Viewmeasurelayout 也是在主线程中调度的,因此 post 中的代码总是会在 measure/layout 之后执行。

  • 代码示例

    View view = findViewById(R.id.my_view);
    view.post(() -> {
        int width = view.getWidth();
        int height = view.getHeight();
    });
    

2. ViewTreeObserver:监听布局变化

  • 原理ViewTreeObserver 允许你监听视图树的全局变化。OnGlobalLayoutListener 在布局发生变化时被调用,OnPreDrawListener 在视图树即将被绘制时调用。

  • 代码示例

    View view = findViewById(R.id.my_view);
    view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            int width = view.getWidth();
            int height = view.getHeight();
        }
    });
    

3. onWindowFocusChanged:窗口焦点变化

  • 原理:当 Activity 窗口获得焦点时,所有 View 都已完成首次布局。

  • 代码示例

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        if (hasFocus) {
            View view = findViewById(R.id.my_view);
            int width = view.getWidth();
            int height = view.getHeight();
        }
    }
    

4. View.onLayout():自定义ViewGroup

  • 原理:对于自定义 ViewGroup,可以在 onLayout 方法中获取其尺寸,并根据尺寸来布局其子 View

  • 代码示例

    public class CustomViewGroup extends ViewGroup {
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            int width = getWidth();
            int height = getHeight();
        }
    }
    

三、View渲染的幕后推手

理解 View 的渲染,需要关注一个关键的幕后角色:ViewRootImpl

  • ViewRootImplActivity 窗口的根。在 ActivityonResume() 之后,ActivityDecorView 被添加到 WindowManager 中,系统会为这个窗口创建一个 ViewRootImpl 实例。
  • ViewRootImpl 负责驱动 measurelayoutdraw 的完整流程。它会与 VSync 信号同步,确保每帧的渲染都在 16.6ms 的时间窗口内完成。

结论

通过理解 View 的测量流程和系统源码,开发者可以确保在正确时机获取尺寸,避免因时机不当导致的错误。