Android - 初始布局后的反应

时间:2012-12-07 07:10:54

标签: android

这个问题已经多次以各种形式提出,但我还没有找到明确的答案。

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

Romain Guy曾暗示我们可以在onMeasure的构造函数中.post Runnable(AFAIK将位于UI线程中)。这似乎适用于大多数设备(在我个人的4个中有3个),但在Nexus S上失败。看起来Nexus S没有任何尺寸,直到每个孩子完成一次通过,并且没有正确的尺寸何时调用View ed post的{​​{1}}方法。我尝试计算布局传递(在Runnable中)并与run进行比较,后者再次在4个中有3个,但是不同的3个(在Droid Razr上失败,似乎只做了一次传递 - 和得到所有测量 - 无论孩子的数量如何)。我尝试了上述的各种排列(正常发布;发布到onLayout;将getChildCount投射到Handler并调用getContext() ...所有人的结果相同。< / p>

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

TYIA

/编辑对我来说,最重要的是Nexus S不会等到布局完成后Activity一个runOnUiThread,就像是这样的情况一样我测试过的所有其他设备,以及Romain Guy和其他人的建议。我还没有找到一个好的解决方法。有吗?

2 个答案:

答案 0 :(得分: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);

答案 1 :(得分: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不同,没有办法删除此覆盖。