4

这个问题已经以各种形式被问过好几次了,但我还没有找到明确的答案。

Views在容器(a contentView)的初始布局之后,我需要能够获得后代的尺寸和位置。在这种情况下所需的尺寸是不可预测的 View(Group) - 可能有多行文本、容纳装饰的最小高度等。我不想在每个布局通道 ( onLayout) 上都这样做。我需要的测量来自一个深度嵌套的孩子,所以覆盖中间每个容器的onLayout/onMeasure似乎是一个糟糕的选择。我不会做任何会导致循环的事情(触发事件期间事件)。

Romain Guy 曾暗示我们可以只在.postaRunnable的构造函数中使用 a View(AFAIK 将在 UI 线程中)。这似乎适用于大多数设备(在我个人拥有的 4 台设备中的 3 台设备上),但在 Nexus S 上失败。看起来 Nexus S 没有所有的尺寸,直到每个孩子完成一次,并且没有正确的尺寸当postedRunnablerun方法被调用时。我尝试计算布局传递(in onLayout)并与 进行比较getChildCount,这同样适用于 4 个中的 3 个,但不同的 3 个(在 Droid Razr 上失败,它似乎只做一次传递 - 并获得所有测量值 - 无论孩子的数量如何)。我尝试了上述的各种排列(正常发布;发布到一个Handler;铸造getContext()到一个Activity并调用runOnUiThread...所有结果相同)。

我最终使用了一个可怕的可耻的无用黑客,通过检查目标群体的高度与其父母的高度 - 如果它不同,则意味着它已经被布置了。显然,这不是最好的方法——但似乎是唯一一种在各种设备和实现之间可靠工作的方法。我知道我们没有可以使用的内置事件或回调,但有更好的方法吗?

TYIA

/EDIT我猜对我来说,最重要的是 Nexus S 不会等到布局完成后.posting()a Runnable,就像我测试过的所有其他设备上的情况一样,正如 Romain Guy 和其他人所建议的那样。我还没有找到一个好的解决方法。有吗?

4

2 回答 2

9

我已经成功使用 OnGlobalLayoutListener。

它在我的 Nexus S 上运行良好

final LinearLayout marker = (LinearLayout) findViewById(R.id.distance_marker);
OnGlobalLayoutListener listener = new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        //some code using marker.getHeight(), etc.
        markersContainer.getViewTreeObserver().removeGlobalOnLayoutListener(this);
    }
};
marker.getViewTreeObserver().addOnGlobalLayoutListener(listener);
于 2012-12-19T01:26:46.497 回答
3

我自己对 OnGlobalLayoutListener 的经验是,它并不总是等到所有子视图都布置好后才触发。因此,如果您正在寻找特定子视图(或子视图的子视图等)的尺寸,那么如果您在 onGlobalLayout() 方法中测试它们,您可能会发现它们反映了布局的一些中间状态进程,而不是显示给用户的最终(静止)状态。

在对此进行测试时,我注意到通过延迟一秒执行 postDelayed() (显然是竞争条件,但这只是为了测试),我确实得到了正确的值(对于子视图的子视图),但是如果我没有延迟地做了一个简单的 post(),我得到了错误的值。

我的解决方案是避免使用 OnGlobalLayoutListener,而是在我的顶级视图(包含我需要测量其孩子的孩子)中直接覆盖 dispatchDraw()。默认的 dispatchDraw() 绘制当前视图的所有子视图,因此如果您在 dispatchDraw() 中调用 super.dispatchDraw(canvas) 后发布对执行测量的代码的调用,那么您可以确保所有子视图在进行测量之前将绘制视图。

看起来是这样的:

@Override
protected void dispatchDraw(Canvas canvas) {
  //Draw the child views
  super.dispatchDraw(canvas);

  //All child views should have been drawn now, so we should be able to take measurements
  // of the global layout here
  post(new Runnable() {
    @Override
    public void run() {
      testAndRespondToChildViewMeasurements();
    }
  });
}

当然,在您进行这些测量之前会进行绘图,而阻止这种情况为时已晚。但如果这不是问题,那么这种方法似乎非常可靠地提供了后代视图的最终(静态)测量。

获得所需的测量值后,您将希望在 testAndRespondToChildViewMeasurements() 顶部设置一个标志,以便它无需进一步执行任何操作(或在 dispatchDraw() 覆盖本身内进行类似测试)即可返回,因为,与 OnGlobalLayoutListener 不同,无法删除此覆盖。

于 2014-01-20T10:52:08.643 回答