我遇到过这种情况两三次,我有一些看起来像这样的代码:
ViewGroup.LayoutParams params = myView.getLayoutParams();
params.height = 240;
myView.requestLayout();
或者:
ViewGroup.LayoutParams params = myView.getLayoutParams();
params.height = 240;
myView.setLayoutParams(params);
视图大小永远不会改变。我试过以下,没有运气:
forceLayout
,具有讽刺意义的似乎不如requestLayout
那么强大,因为它没有通知View的父母需要布局。onMeasure()
以查看是否曾被调用(它不是)。requestLayout
。我也试过在所有View的父母上调用requestLayout
,如下所示:
ViewParent parent = myView.getParent();
while (parent != null) {
parent.requestLayout();
parent = parent.getParent();
}
这很有效,但看起来真的太烂了。我更愿意找到真正的解决方案。
我做错了什么?
答案 0 :(得分:5)
如果您正在观察此行为,您的层次结构中的某些视图可能会请求后台线程上的布局,这是禁忌。
要查找有问题的视图,请进入对requestLayout
的调用,并查找名为mAttachInfo.mViewRequestingLayout
的变量。该引用可能会指向导致不良呼叫的观点。然后,您可以在代码中搜索在此视图中调用requestLayout
或setLayoutParams
的任何位置。
如果您发现并修复了从后台线程进行这些调用的任何情况,这可能会解决问题。
假设您有一个类似于以下内容的视图层次结构:
然后,假设NaughtyView在后台线程上请求布局。
为了让NaughtyView在下一个布局过程中实际得到测量,它需要递归地通知其父级,直到通知视图根:
请注意,每个视图都会在自身上设置一个标志,表示它已请求布局。另请注意,视图根已忽略该请求。但为什么呢?
在某种特定情况下,视图根可能会忽略布局请求,并且它有点微妙。要了解这种情况,请查看ViewRootImpl.requestLayout
:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
简要介绍如何处理布局请求。 You're not supposed to call requestLayout
while a layout pass is in progress。无论如何,如果您这样做,视图根将尝试以通过在第二遍中布置您的视图来满足您的请求。如果mHandlingLayoutInLayoutRequest
成员变量为true,则表示视图根目前正在运行第二次传递。在该阶段,视图根忽略传入的布局请求,因此mLayoutRequested
不会设置为true。
假设所有对requestLayout
的调用都发生在ui线程上,就像他们应该的那样,这种行为是无害的(实际上是理想的)。
但是,如果你在后台线程上调用requestLayout
,它可能会搞乱事件序列并使你的视图层次结构处于类似上图的状态,其中包含一系列拥有它们的祖先"布局要求"标志设置,但视图根不知道它。
接下来,假设有人在NiceView上调用requestLayout
。这次,在ui线程上正确调用:
像往常一样,NiceView尝试通知其所有祖先。但是,一旦此通知与NaughtyView一起到达NiceView的共同祖先,则遍历被阻止。发生这种情况是因为每个视图仅通知其父级,如果其父级尚未得到通知(如上述标志所示)。
由于遍历永远不会到达视图根,因此布局永远不会发生。
如果层次结构中的某些其他视图 - 不的任何视图与视图根目录之外的NaughtyView共享一个祖先 - 本 可以自发修复自身请求布局(正确地,在ui线程上)。发生这种情况时,视图根目录的布局请求" flag将设置为true,它将最终在所有请求它的视图上执行布局(包括NaughtyView和NiceView)。但是,对于某些用户界面,在用户注意到之前,您无法依靠这种情况。