大小更改后,自定义ViewGroup内的视图无法呈现

时间:2011-05-02 01:31:42

标签: android resize rendering custom-view viewgroup

我遇到了一个令我难过的问题,我希望有人可以给我一些指示。

我正在使用一个使用自定义ViewGroup的应用程序(实际上是一个包含RelativeLayout的FrameLayout)来呈现事件日历。日历中的事件表示为视图,其大小根据事件的持续时间和包含视图的大小而定。

我遇到了调整包含FrameLayout大小时出现的问题。当前实现将删除表示事件的所有视图,并尝试添加新事件并根据FrameLayout的当前大小计算其大小。这项工作是通过View的onSizeChanged()方法触发的,该方法在FrameLayout中被覆盖。

调整视图大小时,将执行此代码并更新视图,但是,它们实际上都没有在屏幕上呈现... FrameLayout中包含的视图根本不可见。如果我在hierarchyviewer工具中加载视图,它们是视图树的一部分,并在概述中列出它们应该位于的位置,但它们不会显示。 (请注意,视图在FrameLayout的初始渲染中是可见的......只有在调整大小后它们才会消失。)

调整大小期间的事件顺序如下:

onMeasure()
onMeasure()
onSizeChanged()
onLayout()

重置视图后调用requestLayout()(在onSizeChanged()内)似乎没有任何效果。但是,如果在调用requestLayout()之前导致一些延迟,则视图将变为可见。我可以通过产生一个线程和睡眠来引起这种延迟,或者通过创建一个虚拟按钮来简单地调用requestLayout()并在自己调整大小后按下它,或者甚至将这个丑陋的黑客放在onSizeChanged()的末尾:

post(new Runnable() {
    public void run() {
        requestLayout();
    }
});

当我使用这个hack时,包含的视图是可见的,事件的顺序如下:

onMeasure()
onMeasure()
onSizeChanged()
onLayout()
onMeasure()
onMeasure()
onLayout()

因此,强制第二个度量传递(在视图树被修改之后)似乎使得包含的视图应该是可见的。为什么要延迟对requestLayout()的调用对我来说这是一个谜。

任何人都可以提供关于我做错的指示吗?

我意识到在不查看某些代码的情况下这有点难以理解,因此我创建了一个小型示例应用程序,它展示了我的问题,并在Github上提供了它:

https://github.com/MichaelSims/ViewGroupResizeTest

我上面提到的黑客致力于一个单独的分支:

https://github.com/MichaelSims/ViewGroupResizeTest/tree/post-runnable-hack

如果我可以提供任何其他信息,请提前告知我并提前致谢。

2 个答案:

答案 0 :(得分:25)

这不是一个黑客,它应该是如何工作的。 onSizeChanged()作为布局传递的一部分被调用,并且在布局传递期间你不能/不应该requestLayout()。在事件队列上发布requestLayout()以指示您在布局传递期间更改了View层次结构是正确的。

答案 1 :(得分:0)

我遇到了同样的问题。

我的onMeasure()是这样的:

@Override    
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ...
        //calculate new width and height here
        ...
        setMeasuredDimension(newMeasureSpecWidth, newMeasureSpecHeight);
    }

我的错误是我在onMeasure()的第一行调用了super.onMeasure(),所以我的ViewGroup的内部子节点是根据即将发生变化的大小来计算的。

所以我的修复就是做这样的事情:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    //calculate new width and height here
    ...
    int newMeasureSpecWidth = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY);
    int newMeasureSpecHeight = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
    super.onMeasure(newMeasureSpecWidth, newMeasureSpecHeight);
}

所以我使用对super.onMeasure()的调用将新大小设置为我的ViewGroup,然后它还将新大小传递给它的子节点。

记住:当你覆盖onMeasure()时,你必须调用setMeasuredDimension()(你可以通过调用方法本身或通过调用super.onMeasure()来实现这一点) < / p>