如何绘制布局时如何判断?

Pla*_*eon 194 android android-layout

我有一个自定义视图,可以将可滚动的位图绘制到屏幕上.为了初始化它,我需要传递父布局对象的像素大小.但是在onCreate和onResume函数期间,尚未绘制布局,因此layout.getMeasuredHeight()返回0.

作为一种解决方法,我添加了一个处理程序等待一秒然后测量.这是有效的,但它很草率,我不知道在布局绘制之前我能在多长时间内缩短时间.

我想知道的是,如何检测布局何时被绘制?有事件或回调吗?

ble*_*enm 381

您可以将树观察器添加到布局中.这应该返回正确的宽度和高度.onCreate()在完成子视图的布局之前调用.所以宽度和高度尚未计算.要获得高度和宽度,请将其放在onCreate()方法上:

    final LinearLayout layout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);
    ViewTreeObserver vto = layout.getViewTreeObserver(); 
    vto.addOnGlobalLayoutListener (new OnGlobalLayoutListener() { 
        @Override 
        public void onGlobalLayout() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    layout.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    layout.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
            int width  = layout.getMeasuredWidth();
            int height = layout.getMeasuredHeight(); 

        } 
    });
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢你!但我想知道为什么没有更简单的方法来做这个,因为这是一个常见的问题. (25认同)
  • 在API级别16中不推荐使用removeGlobalOnLayoutListener.请改用removeOnGlobalLayoutListener. (8认同)
  • 我看到的一个"问题"是,如果您的视图不可见,则调用onGlobalLayout,但稍后在可见时调用getView,而不是onGlobalLayout ...呃. (3认同)
  • 另一个快速解决方案是使用“view.post(Runnable)”,它更干净并且可以做同样的事情。 (2认同)

Ell*_*ica 63

一个非常简单的方法就是简单地调用post()你的布局.在您的视图创建完成后,这将在下一步运行您的代码.

YOUR_LAYOUT.post( new Runnable() {
    @Override
    public void run() {
        int width  = YOUR_LAYOUT.getMeasuredWidth();
        int height = YOUR_LAYOUT.getMeasuredHeight(); 
    }
});
Run Code Online (Sandbox Code Playgroud)

  • 相反,post()在下一个UI步骤更新后执行,这在创建视图后发生.因此,保证在查看完成后它将执行. (4认同)
  • 我不知道post是否会在创建视图后实际运行代码.无法保证,因为post只会将您创建的Runnable添加到RunQueue,以便在UI Thread上执行上一个队列之后执行. (3认同)
  • @HendraWijayaDjiono,也许这篇文章将回答:http://stackoverflow.com/questions/21937224/does-posting-runnable-to-an-ui-thread-guarantee-that-layout-is-finished-when-it (3认同)
  • 这个答案简直太棒了!它工作,它使我的一天. (3认同)

小智 21

为避免弃用代码和警告,您可以使用:

view.getViewTreeObserver().addOnGlobalLayoutListener(
        new ViewTreeObserver.OnGlobalLayoutListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    view.getViewTreeObserver()
                            .removeOnGlobalLayoutListener(this);
                } else {
                    view.getViewTreeObserver()
                            .removeGlobalOnLayoutListener(this);
                }
                yourFunctionHere();
            }
        });
Run Code Online (Sandbox Code Playgroud)

  • 使用此条件而不是Build.VERSION.SDK_INT <Build.VERSION_CODES.JELLY_BEAN (2认同)

Vin*_*gas 13

androidx.core-ktx 已经有这个

/**
 * Performs the given action when this view is next laid out.
 *
 * The action will only be invoked once on the next layout and then removed.
 *
 * @see doOnLayout
 */
public inline fun View.doOnNextLayout(crossinline action: (view: View) -> Unit) {
    addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
        override fun onLayoutChange(
            view: View,
            left: Int,
            top: Int,
            right: Int,
            bottom: Int,
            oldLeft: Int,
            oldTop: Int,
            oldRight: Int,
            oldBottom: Int
        ) {
            view.removeOnLayoutChangeListener(this)
            action(view)
        }
    })
}

/**
 * Performs the given action when this view is laid out. If the view has been laid out and it
 * has not requested a layout, the action will be performed straight away, otherwise the
 * action will be performed after the view is next laid out.
 *
 * The action will only be invoked once on the next layout and then removed.
 *
 * @see doOnNextLayout
 */
public inline fun View.doOnLayout(crossinline action: (view: View) -> Unit) {
    if (ViewCompat.isLaidOut(this) && !isLayoutRequested) {
        action(this)
    } else {
        doOnNextLayout {
            action(it)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Ser*_*gio 12

使用 kotlin 扩展功能更好

inline fun View.waitForLayout(crossinline yourAction: () -> Unit) {
    val vto = viewTreeObserver
    vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            when {
                vto.isAlive -> {
                    vto.removeOnGlobalLayoutListener(this)
                    yourAction()
                }
                else -> viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        }
    })
}
Run Code Online (Sandbox Code Playgroud)