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

时间:2011-10-11 23:47:05

标签: android android-layout

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

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

我想知道的是,如何检测布局何时被绘制?是否有活动或回电?

9 个答案:

答案 0 :(得分:366)

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

final LinearLayout layout = (LinearLayout) findViewById(R.id.YOUR_VIEW_ID);
ViewTreeObserver vto = layout.getViewTreeObserver(); 
vto.addOnGlobalLayoutListener (new OnGlobalLayoutListener() { 
    @Override 
    public void onGlobalLayout() {
        layout.getViewTreeObserver().removeOnGlobalLayoutListener(this); 
        int width  = layout.getMeasuredWidth();
        int height = layout.getMeasuredHeight(); 

    } 
});

答案 1 :(得分:53)

一种非常简单的方法是简单地在布局上调用YOUR_LAYOUT.post( new Runnable() { @Override public void run() { int width = YOUR_LAYOUT.getMeasuredWidth(); int height = YOUR_LAYOUT.getMeasuredHeight(); } }); 。在您的视图创建后,这将在下一步运行您的代码。

Y-m-d\TH:m

答案 2 :(得分: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();
            }
        });

答案 3 :(得分:3)

另一个答案是:
请尝试查看onWindowFocusChanged处的查看尺寸。

答案 4 :(得分:3)

通常方法的替代方法是挂钩视图的绘图。

显示视图时多次调用

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
    }
});

答案 5 :(得分:3)

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

答案 6 :(得分:2)

只需在布局或视图上调用post方法即可对其进行检查

 view.post( new Runnable() {
 @Override
 public void run() {
    // your layout is now drawn completely , use it here.
  }
});

答案 7 :(得分:1)

当调用onMeasure时,视图会获得其测量的宽度/高度。在此之后,您可以拨打layout.getMeasuredHeight()

答案 8 :(得分:0)

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