将视图添加到ViewGroup时可以跳过onMeasure吗?

时间:2011-06-09 20:26:49

标签: android layout viewgroup

回答

我有一个RelativeLayout,我会在用户垂直或水平滚动时动态添加视图。我已经推出了自己的ViewRecycler,因为可能有数千个视图可以构成可以滚动的整个视图,但我只能在任何时候显示30个左右。想一想日历的放大视图。

当我添加即将看到的视图时,我遇到了性能问题,在RelativeLayout上调用onMeasure,并在其所有子视图上调用onMeasure。我已经计算了RelativeLayout有多大的计算大小,并在它的LayoutParameters上设置了它,因此测量ViewGroup不是必需的,也不是重新测量已经添加了最终大小的视图和新的添加的视图与这些视图无关。

演示问题的简单示例是向RelativeLayout添加/删除View并观察onMeasure被调用,尽管它不会影响RelativeLayout的大小或其他视图的位置。

main.xml中

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/shell"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
</LinearLayout>

MyActivity.java

public class MyActivity extends Activity
{

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ViewGroup shell = (ViewGroup) findViewById(R.id.shell);

        final RelativeLayout container = new RelativeLayout(this) {
            @Override
            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                Log.d("MyActvity", "onMeasure called on map");
            }
        };
        container.setBackgroundColor(Color.rgb(255, 0, 0));
        ViewGroup.LayoutParams containerParams = new ViewGroup.LayoutParams(300, 300);

        final TextView childView = new TextView(this);
        childView.setBackgroundColor(Color.rgb(0, 255, 0));
        childView.setText("Child View");

        Button viewToggle = (Button) findViewById(R.id.button);
        viewToggle.setText("Add/Remove Child View");

        viewToggle.setOnClickListener(new View.OnClickListener() {

            public void onClick(View view) {
                if (childView.getParent() == null) {
                    container.addView(childView, 400, 30);
                } else {
                   container.removeView(childView);
                }
            }
        });

        shell.addView(container, containerParams);
    }
}

运行此选项,您将看到对onMeasure的2个初始(预期)调用,然后每次通过单击按钮添加/删除视图时调用一个。这显然运行良好,但是当你有一个复杂的嵌套视图布局时,你可以看到onMeasure的常量调用可能会出现问题。

是否有推荐的方法绕过这些onMeasure电话或至少onMeasure调用measureChildren?

3 个答案:

答案 0 :(得分:0)

我没有滚动自己的布局管理器(我将来可能会做),而是将onMeasure更改为:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();
    for (int i = 0; count > i; i++) {
        View v = getChildAt(i);

        if (v.getVisibility() != GONE) {
            if (v.getMeasuredWidth() <= 0 || v.getMeasuredHeight() <= 0) {
                measureChild(v,
                    MeasureSpec.makeMeasureSpec(v.getLayoutParams().width,
                        MeasureSpec.AT_MOST),
                    MeasureSpec.makeMeasureSpec(v.getLayoutParams().height,
                        MeasureSpec.AT_MOST));
            }
        }
    }

    setMeasuredDimension(resolveSize(staticContainerWidth, widthMeasureSpec),
        resolveSize(staticContainerHeight, heightMeasureSpec));
}

...并为容器添加了一个sudo-hard编码的高度和宽度作为变量。将这些设置为您期望的范围超出了此解决方案的范围。

int staticContainerHeight = 300;
int staticContainerWidth = 300;

答案 1 :(得分:0)

当动画发生在viewgroup的大小时,我遇到了类似的问题,其onMeasure()被非常频繁地调用。由于父视图包含许多子视图,因此频繁级联的onMeasure()调用会导致动画性能打嗝。我有另一种肮脏的解决方案,但比推出我自己的layoutManager简单得多。

long mLastOnMeasurTimestamp;
...
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    long currentTimestamp = System.currentTimeMillis();
    if(currentTimestamp - mLastOnMeasureTimestamp < SKIP_PERIOD_IN_MILL){
        return;
    }
    mLastOnMeasureTimestamp = currentTimestamp;
    ...

答案 2 :(得分:0)

我遇到了类似的问题,我的解决方案是检查尺寸是否已更改:

int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(parentWidth, parentHeight);
if ( mClientWidth == parentWidth && mClientHeight == parentHeight ) {
    return;
}

mClientWidth = parentWidth;
mClientHeight = parentHeight;

因此,如果父级的维度没有真正改变,则不会将其级联到其子级。