4

In my Activity I have a GridView inflated from this XML layout:

<GridView
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:columnWidth="@dimen/thumbnail_size"
    android:gravity="fill"
    android:horizontalSpacing="16dp"
    android:numColumns="auto_fit"
    android:stretchMode="columnWidth"
    android:verticalSpacing="16dp" />

The items inside it are RelativeLayouts, each set to a fixed height. While profiling my Activity, I noticed that when I enter an ActionMode, it causes a measure pass, which causes the GridView to be re-measured. The GridView in turn calls getView() on my adapter many times to measure all of the child views. This is unnecessary work, since (a) which children are visible hasn't changed, so it already has all the views it needs; and (b) none of the child views can change size.

I know that ListView and GridView have lots of tricks they can do to avoid work in easy cases, and this seems like the kind of special case they might have an optimization for. Is there something I can set on the GridView to help it realise there's no need to call getView() again here? Alternatively, can I go higher up the stack and avoid the measure pass completely when entering and leaving an ActionMode? Just to be clear, the behaviour of my adapter is correct, and the getView() calls aren't causing me a problem: I just want my app to burn fewer cycles.

N.B. Even if I set the items to have fixed width as well as height, and replace the GridView with the following, it doesn't change the number of getView() calls (but it does make my Activity ugly).

<GridView
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:descendantFocusability="beforeDescendants"
    android:columnWidth="@dimen/thumbnail_size"
    android:horizontalSpacing="16dp"
    android:numColumns="3"
    android:stretchMode="none"
    android:verticalSpacing="16dp" />
4

3 回答 3

2

Use ListView instead of GridView. Look at the implementation (API 16):

ListView.onMeasure(...) {
...
    if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED ||
            heightMode == MeasureSpec.UNSPECIFIED)) {
        final View child = obtainView(0, mIsScrap);
...
}

and

GridView.onMeasure(...) {
...
    final int count = mItemCount;
    if (count > 0) {
        final View child = obtainView(0, mIsScrap);
...
}

It looks like a bug.

于 2013-08-09T10:50:17.123 回答
2

Some generic ways to make GridView perform better:

  • Most important: Make your getView() as fast as possible. For example, move any IO or heavy computation away from UI thread.

  • For measure/layout purposes, it seems that the view in position 0 is always requested. You can cache this view yourself to return it super fast in getView().

  • Get rid of any possible LinearLayouts with layout_weight as each nesting level there doubles the number of measure/layout passes required.

于 2013-08-09T11:18:04.387 回答
1

how about the next solution:

add a flag in the adapter that once set, in the getView(), it will return the convertView without any changes.

if the user has touched the listView, set the flag to false.

if something has caused the listView to change its size (for example the actionMode) , set it to true.

EDIT: you will have to check that everything seems to be in order - that each view still shows the same item that it has shown before. it's not just "return the convertView". it depends on what has occurred.

于 2013-03-31T08:36:54.793 回答