即使在调用requestLayout之后,在Android视图上更改LayoutParams也没有任何效果

fps*_*li3 4 android android-layout android-measure

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

  ViewGroup.LayoutParams params = myView.getLayoutParams();
  params.height = 240;
  myView.requestLayout();
Run Code Online (Sandbox Code Playgroud)

或者:

  ViewGroup.LayoutParams params = myView.getLayoutParams();
  params.height = 240;
  myView.setLayoutParams(params);
Run Code Online (Sandbox Code Playgroud)

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

  1. 呼叫forceLayout,具有讽刺意味的似乎不那么有力requestLayout,因为它没有通知View的父母需要布局.
  2. 覆盖onMeasure()以查看是否曾被调用(它不是).
  3. 确保我requestLayout在布局过程中没有进行调用.

我也尝试过调用requestLayout所有View的父母,如下所示:

  ViewParent parent = myView.getParent();
  while (parent != null) {
      parent.requestLayout();
      parent = parent.getParent();
  }
Run Code Online (Sandbox Code Playgroud)

这有效,但看起来真的很酷.我更愿意找到真正的解决方案.

我究竟做错了什么?

fps*_*li3 5

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

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

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

背景/解释

假设您有一个看起来像这样的视图层次结构:

查看层次结构

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

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

查看根忽略请求

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

在某种特定情况下,视图根可能会忽略布局请求,并且它有点微妙.为了看到这种情况,让我们来看看实现ViewRootImpl.requestLayout:

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
Run Code Online (Sandbox Code Playgroud)

简要介绍如何处理布局请求.布局传递正在进行时,您不应该打电话requestLayout.无论如何,如果您这样做,视图根将通过在第二遍中布置您的视图来尝试满足您的请求.如果mHandlingLayoutInLayoutRequest成员变量为true,则表示视图根目前正在运行第二次传递.在该阶段,视图根忽略传入的布局请求,因此mLayoutRequested不会设置为true.

假设所有调用requestLayout都发生在ui线程上,就像它们应该的那样,这种行为是无害的(实际上是期望的).

但是,如果您拨打电话到requestLayout在后台线程,它可以搞砸事件的顺序并留下您的视图层次的状态类似上图中,与谁拥有自己的"布局要求"标志设置祖先的字符串,但一个不知道它的视图根.

接下来,假设有人打电话requestLayout给NiceView.这次,在ui线程上正确调用:

布局请求被阻止

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

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

这个问题可以自发地自我修复,如果有的在其他层次的观点-即没有任何视图共享NaughtyView比视图根以外的祖先-恰好请求布局(正常,UI线程).发生这种情况时,视图根自己的"布局要求"标志将被设置为true,并最终将所有的都要求它的意见(包括NaughtyView和NICEVIEW)进行布局.但是,对于某些用户界面,在用户注意到之前,您无法指望这种情况发生.