我有一个自定义视图,绘制一个可滚动的位图到屏幕。为了初始化它,我需要传入父布局对象的像素大小。但是在onCreate和onResume函数期间,布局还没有绘制,因此Layout . getmeasuredheight()返回0。
作为一种变通方法,我添加了一个处理程序来等待一秒钟,然后进行测量。这是工作,但它草率,我不知道多少我可以修剪的时间之前,我结束之前的布局得到绘制。
我想知道的是,我如何检测当一个布局被绘制?是否有事件或回调?
我有一个自定义视图,绘制一个可滚动的位图到屏幕。为了初始化它,我需要传入父布局对象的像素大小。但是在onCreate和onResume函数期间,布局还没有绘制,因此Layout . getmeasuredheight()返回0。
作为一种变通方法,我添加了一个处理程序来等待一秒钟,然后进行测量。这是工作,但它草率,我不知道多少我可以修剪的时间之前,我结束之前的布局得到绘制。
我想知道的是,我如何检测当一个布局被绘制?是否有事件或回调?
您可以在布局中添加一个树形观察器。这将返回正确的宽度和高度。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();
}
});
要避免弃用代码和警告,您可以使用:
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() <= 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
}
});
使用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)
}
}
}
最优雅的方法是使用OneShotPreDrawListener。OneShotPreDrawListener将注册一个回调函数,在视图树即将被绘制时调用,并且它将在一次OnPreDraw调用后删除自己。
OneShotPreDrawListener.add(view) {
view.doSomething();
}
我创建静态变量来检查布局是否已经绘制。如果它已经创建,你可以简单地调用绘制的函数。
static int firstAccess = 0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main8);
if (firstAccess==0) {
LAYOUT_YOU_DRAW.post(new Runnable() {
@Override
public void run() {
creatSpread();
firstAccess = 1;
}
});
}else{
creatSpread();
}
}
如果您正在使用kotlin,我建议您使用doOnLayout
doOnLayout {
doWhateverYouNeed()
}
道格: 在布局此视图时执行给定的操作