Android 您如何判断何时绘制了布局?
我有一个自定义视图,可以在屏幕上绘制一个可滚动的位图。为了初始化它,我需要传入父布局对象的大小(以像素为单位)。但是在onCreate和onResume函数期间,布局尚未绘制,因此Layout.getMeasuredHeight()返回0 作为一种解决方法,我添加了一个处理程序来等待一秒钟,然后进行测量。这是可行的,但它太草率了,我不知道在绘制布局之前,我能缩短多少时间Android 您如何判断何时绘制了布局?,android,android-layout,Android,Android Layout,我有一个自定义视图,可以在屏幕上绘制一个可滚动的位图。为了初始化它,我需要传入父布局对象的大小(以像素为单位)。但是在onCreate和onResume函数期间,布局尚未绘制,因此Layout.getMeasuredHeight()返回0 作为一种解决方法,我添加了一个处理程序来等待一秒钟,然后进行测量。这是可行的,但它太草率了,我不知道在绘制布局之前,我能缩短多少时间 我想知道的是,如何检测布局何时绘制?是否存在事件或回调?您可以将树观察者添加到布局中。这将返回正确的宽度和高度onCreate
我想知道的是,如何检测布局何时绘制?是否存在事件或回调?您可以将树观察者添加到布局中。这将返回正确的宽度和高度<在完成子视图的布局之前调用code>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();
}
});
调用测量时,视图将获得其测量的宽度/高度。在此之后,您可以调用
layout.getMeasuredHeight()
另一个答案是:请尝试在WindowFocusChanged上检查视图尺寸,以避免使用不推荐的代码和警告:
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();
}
});
一个非常简单的方法是在布局上调用
post()
。这将在下一步创建视图后运行代码
YOUR_LAYOUT.post( new Runnable() {
@Override
public void run() {
int width = YOUR_LAYOUT.getMeasuredWidth();
int height = YOUR_LAYOUT.getMeasuredHeight();
}
});
通常方法的替代方法是挂接视图的图形
显示视图时会多次调用
OnPreDrawListener
,因此在视图具有有效测量宽度或高度的情况下,没有特定的迭代。这要求您不断地验证(view.getMeasuredWidth()),只需在布局或视图上调用post方法即可进行检查
view.post( new Runnable() {
@Override
public void run() {
// your layout is now drawn completely , use it here.
}
});
使用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)
}
}
})
}
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)
}
}
}
您不需要创建自己的自定义视图才能知道onMeasure何时触发吗?是的,您必须使用自定义视图来访问度量值。非常感谢!但我想知道为什么没有更简单的方法来执行此操作,因为这是一个常见的问题。太棒了。我只尝试了LayoutChangeListener,但它不起作用。但是OnGlobalYoutListener成功了。非常感谢很多!RemoveGlobalOnlayOutliner在API级别16中被弃用。请改用removeOnGlobalLayoutListener。我看到的一个“问题”是,如果视图不可见,则调用onGlobalLayout,但稍后在可见时调用getView,而不是onGlobalLayout…呃。另一个快速解决方案是使用view.post(Runnable)
,它更干净,并且做同样的事情。为了使用此代码,“视图”必须声明为“最终”。使用此条件,而不是Build.VERSION.SDK\u INT如何判断何时绘制了布局?
并且您提供了一个方法,在该方法中调用OnPreDrawListener
并且在该视图向您提供合理的答案
因此,对您的解决方案的反对意见应该是显而易见的。您需要知道的不是何时绘制,而是何时测量。我的代码回答了真正需要问的问题。此解决方案是否存在问题?它是否无法解决所面临的问题?视图在放置之前不会被测量t、 所以这个替代方案是多余的,并且执行了冗余检查。你承认没有解决问题,但是你觉得应该问什么。最初的评论提出了这两个问题,但是作为编辑提交的代码本身也存在一些问题。这是一个很好的编辑。谢谢。我想我问的是,你认为呢这个答案是否应该被删除?我遇到了OP的问题。这解决了问题。我真诚地提供了它。这是一个错误吗?这是不应该在StackOverflow上做的事情吗?非常好和优雅的解决方案,可以重复使用。这不是真的,可能会隐藏可能的问题。post
只会对运行进行排队如果无法在之后运行,则无法确保视图已测量,甚至无法绘制。@IonutNegru可能会阅读此@BrunoBieri,您可以使用更改视图测量值的异步函数测试此行为,并查看队列中的任务是否在每次测量后运行并收到预期值。
/**
* 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)
}
}
}