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

作为一种变通方法,我添加了一个处理程序来等待一秒钟,然后进行测量。这是工作,但它草率,我不知道多少我可以修剪的时间之前,我结束之前的布局得到绘制。

我想知道的是,我如何检测当一个布局被绘制?是否有事件或回调?


当前回答

通常方法的另一种替代方法是钩入视图的绘图。

OnPreDrawListener在显示视图时被多次调用,因此没有特定的迭代,其中您的视图具有有效的测量宽度或高度。这要求您不断地验证(view.getMeasuredWidth() <= 0)或设置检查measuredWidth大于零的次数限制。

也有可能永远不会绘制视图,这可能表明您的代码存在其他问题。

final View view = [ACQUIRE REFERENCE]; // Must be declared final for inner class
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        if (view.getMeasuredWidth() > 0) {     
            view.getViewTreeObserver().removeOnPreDrawListener(this);
            int width = view.getMeasuredWidth();
            int height = view.getMeasuredHeight();
            //Do something with width and height here!
        }
        return true; // Continue with the draw pass, as not to stop it
    }
});

其他回答

一个非常简单的方法是在布局上调用post()。这将在创建视图之后运行您的代码。

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

当onMeasure被调用时,视图得到它测量的宽度/高度。在此之后,你可以调用layout.getMeasuredHeight()。

使用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)
            }
        }
    })
}

通常方法的另一种替代方法是钩入视图的绘图。

OnPreDrawListener在显示视图时被多次调用,因此没有特定的迭代,其中您的视图具有有效的测量宽度或高度。这要求您不断地验证(view.getMeasuredWidth() <= 0)或设置检查measuredWidth大于零的次数限制。

也有可能永远不会绘制视图,这可能表明您的代码存在其他问题。

final View view = [ACQUIRE REFERENCE]; // Must be declared final for inner class
ViewTreeObserver viewTreeObserver = view.getViewTreeObserver();
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        if (view.getMeasuredWidth() > 0) {     
            view.getViewTreeObserver().removeOnPreDrawListener(this);
            int width = view.getMeasuredWidth();
            int height = view.getMeasuredHeight();
            //Do something with width and height here!
        }
        return true; // Continue with the draw pass, as not to stop it
    }
});

最优雅的方法是使用OneShotPreDrawListener。OneShotPreDrawListener将注册一个回调函数,在视图树即将被绘制时调用,并且它将在一次OnPreDraw调用后删除自己。

OneShotPreDrawListener.add(view) {
    view.doSomething();
}