即使在调用requestLayout

时间:2017-07-02 16:36:16

标签: android android-layout android-measure

我遇到过这种情况两三次,我有一些看起来像这样的代码:

  ViewGroup.LayoutParams params = myView.getLayoutParams();
  params.height = 240;
  myView.requestLayout();

或者:

  ViewGroup.LayoutParams params = myView.getLayoutParams();
  params.height = 240;
  myView.setLayoutParams(params);

视图大小永远不会改变。我试过以下,没有运气:

  1. 拨打forceLayout,具有讽刺意义的似乎不如requestLayout那么强大,因为它没有通知View的父母需要布局。
  2. 覆盖onMeasure()以查看是否曾被调用(它不是)。
  3. 确保在布局过程中我没有拨打requestLayout
  4. 我也试过在所有View的父母上调用requestLayout,如下所示:

      ViewParent parent = myView.getParent();
      while (parent != null) {
          parent.requestLayout();
          parent = parent.getParent();
      }
    

    这很有效,但看起来真的太烂了。我更愿意找到真正的解决方案。

    我做错了什么?

1 个答案:

答案 0 :(得分:5)

如果您正在观察此行为,您的层次结构中的某些视图可能会请求后台线程上的布局,这是禁忌。

要查找有问题的视图,请进入对requestLayout的调用,并查找名为mAttachInfo.mViewRequestingLayout的变量。该引用可能会指向导致不良呼叫的观点。然后,您可以在代码中搜索在此视图中调用requestLayoutsetLayoutParams的任何位置。

如果您发现并修复了从后台线程进行这些调用的任何情况,这可能会解决问题。

背景/解释

假设您有一个类似于以下内容的视图层次结构:

view hierarchy

然后,假设NaughtyView在后台线程上请求布局。

为了让NaughtyView在下一个布局过程中实际得到测量,它需要递归地通知其父级,直到通知视图根:

view root ignores request

请注意,每个视图都会在自身上设置一个标志,表示它已请求布局。另请注意,视图根已忽略该请求。但为什么呢?

在某种特定情况下,视图根可能会忽略布局请求,并且它有点微妙。要了解这种情况,请查看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线程上正确调用:

layout request blocked

像往常一样,NiceView尝试通知其所有祖先。但是,一旦此通知与NaughtyView一起到达NiceView的共同祖先,则遍历被阻止。发生这种情况是因为每个视图仅通知其父级,如果其父级尚未得到通知(如上述标志所示)。

由于遍历永远不会到达视图根,因此布局永远不会发生。

如果层次结构中的某些其他视图 - 的任何视图与视图根目录之外的NaughtyView共享一个祖先 - 本 可以自发修复自身请求布局(正确地,在ui线程上)。发生这种情况时,视图根目录的布局请求" flag将设置为true,它将最终在所有请求它的视图上执行布局(包括NaughtyView和NiceView)。但是,对于某些用户界面,在用户注意到之前,您无法依靠这种情况。