Android-在初始布局后做出反应

Android-在初始布局后做出反应,android,Android,这个问题以各种形式被问了好几次,但我还没有找到一个明确的答案 我需要能够在容器的初始布局(acontentView)之后获得子体视图的尺寸和位置。这种情况下所需的尺寸是不可预测的视图(组)-可能有多行文字、容纳装饰的最小高度等。我不希望在每个布局过程中都这样做(onLayout)。我需要的度量来自一个深度嵌套的子容器,因此覆盖中间每个容器的onLayout/onMeasure似乎是一个糟糕的选择。我不会做任何会导致循环的事情(事件期间触发事件) Romain Guy曾经暗示,我们可以在视图的构造

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

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

Romain Guy曾经暗示,我们可以在
视图的构造函数中发布
.post
a
Runnable
(这可能在UI线程中)。这似乎适用于大多数设备(在我个人拥有的4台设备中的3台设备上),但在Nexus S上失败。似乎Nexus S在每个孩子完成一次操作之前没有所有的维度,并且在调用
post
ed
Runnable
run方法时没有正确的维度。我尝试计算布局过程(在
onLayout
中)并与
getChildCount
进行比较,这同样适用于4个中的3个,但不同的3个(在Droid Razr上失败,它似乎只完成一个过程-并获得所有测量值-无论孩子的数量如何)。我尝试了上述各种排列方式(正常发布;发布到
处理程序
;将
getContext()
转换到
活动
并调用
runOnUiThread
…所有人的结果相同)

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

蒂亚


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

我已经成功地使用了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);

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

在测试过程中,我注意到,通过执行具有1秒延迟的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不同,无法删除此覆盖。

在执行所有布局后,我需要只发生一次。我相信每次有变化的时候,OnGlobalYoutListener都会开火,对吗?如果是这样的话,有没有办法使用您发布的内容来确保所有“正常”布局都是完整的?谢谢。像我在onGlobalLayout末尾那样删除侦听器意味着只能执行一次。对我来说,所有正常的布局都是在一个非常复杂的视图中完成的,但我必须阅读更多/更好地了解您的情况,以保证更多。似乎有效,谢谢。我将把这个问题留待几天,以防有更“标准”的解决方案,但如果没有,我将接受并奖励+1现在。OnGlobalLayoutListener,你最好的朋友,也是书中最古老的把戏看一看,因为测量可能会快一点,特别是当你打算修复某些东西并导致另一个无效IO/布局时。这太棒了-我很高兴尝试一下。谢谢。