3

最近,我发现ListView展开/折叠动画DialogFragmentActivity.

DialogFragment中,动画 FPS 为 10 fps。每个动画需要 100 毫秒。

Activity中,动画 FPS 为 50 fps。每个动画需要 20 毫秒。

我把这个很棒的开源库作为基准示例:https ://github.com/nhaarman/ListViewAnimations

我在https://github.com/nhaarman/ListViewAnimations/blob/master/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ItemManipulationsExamplesActivity.java#L51上进行了修改,以测试DialogFragment和之间的性能Activity

    public void onExpandListItemAdapterClicked(View view) {
        // For Activity testing.
        Intent intent = new Intent(this, ExpandableListItemActivity.class);
        startActivity(intent);

        // For DialogFragment testing.
        //FragmentManager fm = this.getSupportFragmentManager();
        //DemoDialogFragment demoDialogFragment = new DemoDialogFragment();
        //demoDialogFragment.show(fm, "DemoDialogFragment");        
    }

我对https://github.com/nhaarman/ListViewAnimations/blob/master/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ExpandableListItemActivity.java做了一个小的修改

public class ExpandableListItemActivity extends MyListActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MyExpandableListItemAdapter myExpandableListItemAdapter = new MyExpandableListItemAdapter(this, getItems());
        //AlphaInAnimationAdapter alphaInAnimationAdapter = new AlphaInAnimationAdapter(myExpandableListItemAdapter);
        //alphaInAnimationAdapter.setAbsListView(getListView());
        getListView().setAdapter(myExpandableListItemAdapter);

        Toast.makeText(this, R.string.explainexpand, Toast.LENGTH_LONG).show();
    }

我创建了自己DialogFragment的测试类。完全基于ExpandableListItemActivity

package com.haarman.listviewanimations.itemmanipulationexamples;

import java.util.ArrayList;
import java.util.List;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.haarman.listviewanimations.ArrayAdapter;
import com.haarman.listviewanimations.R;
import com.haarman.listviewanimations.itemmanipulation.ExpandableListItemAdapter;

public class DemoDialogFragment extends DialogFragment {

    private ListView mListView;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mListView = new ListView(getActivity());
        mListView.setDivider(null);

        MyExpandableListItemAdapter myExpandableListItemAdapter = new MyExpandableListItemAdapter(getActivity(), getItems());
        //AlphaInAnimationAdapter alphaInAnimationAdapter = new AlphaInAnimationAdapter(myExpandableListItemAdapter);
        //alphaInAnimationAdapter.setAbsListView(getListView());
        getListView().setAdapter(myExpandableListItemAdapter);

        Toast.makeText(getActivity(), R.string.explainexpand, Toast.LENGTH_LONG).show();

        final AlertDialog dialog = new AlertDialog.Builder(this.getActivity())            
        .setView(mListView)
        .create();

        return dialog;
    }

    public ListView getListView() {
        return mListView;
    }

    protected ArrayAdapter<Integer> createListAdapter() {
        return new MyListAdapter(getActivity(), getItems());
    }

    public static ArrayList<Integer> getItems() {
        ArrayList<Integer> items = new ArrayList<Integer>();
        for (int i = 0; i < 1000; i++) {
            items.add(i);
        }
        return items;
    }

    private static class MyListAdapter extends ArrayAdapter<Integer> {

        private Context mContext;

        public MyListAdapter(Context context, ArrayList<Integer> items) {
            super(items);
            mContext = context;
        }

        @Override
        public long getItemId(int position) {
            return getItem(position).hashCode();
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView tv = (TextView) convertView;
            if (tv == null) {
                tv = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_row, parent, false);
            }
            tv.setText("This is row number " + getItem(position));
            return tv;
        }
    }

    private static class MyExpandableListItemAdapter extends ExpandableListItemAdapter<Integer> {

        private Context mContext;
        private LruCache<Integer, Bitmap> mMemoryCache;

        /**
         * Creates a new ExpandableListItemAdapter with the specified list, or an empty list if
         * items == null.
         */
        private MyExpandableListItemAdapter(Context context, List<Integer> items) {
            super(context, R.layout.activity_expandablelistitem_card, R.id.activity_expandablelistitem_card_title, R.id.activity_expandablelistitem_card_content, items);
            mContext = context;

            final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

            // Use 1/8th of the available memory for this memory cache.
            final int cacheSize = maxMemory;
            mMemoryCache = new LruCache<Integer, Bitmap>(cacheSize) {
                @Override
                protected int sizeOf(Integer key, Bitmap bitmap) {
                    // The cache size will be measured in kilobytes rather than
                    // number of items.
                    return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
                }
            };
        }

        @Override
        public View getTitleView(int position, View convertView, ViewGroup parent) {
            TextView tv = (TextView) convertView;
            if (tv == null) {
                tv = new TextView(mContext);
            }
            tv.setText(mContext.getString(R.string.expandorcollapsecard, getItem(position)));
            return tv;
        }

        @Override
        public View getContentView(int position, View convertView, ViewGroup parent) {
            ImageView imageView = (ImageView) convertView;
            if (imageView == null) {
                imageView = new ImageView(mContext);
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            }

            int imageResId;
            switch (getItem(position) % 5) {
            case 0:
                imageResId = R.drawable.img_nature1;
                break;
            case 1:
                imageResId = R.drawable.img_nature2;
                break;
            case 2:
                imageResId = R.drawable.img_nature3;
                break;
            case 3:
                imageResId = R.drawable.img_nature4;
                break;
            default:
                imageResId = R.drawable.img_nature5;
            }

            Bitmap bitmap = getBitmapFromMemCache(imageResId);
            if (bitmap == null) {
                bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResId);
                addBitmapToMemoryCache(imageResId, bitmap);
            }
            imageView.setImageBitmap(bitmap);

            return imageView;
        }

        private void addBitmapToMemoryCache(int key, Bitmap bitmap) {
            if (getBitmapFromMemCache(key) == null) {
                mMemoryCache.put(key, bitmap);
            }
        }

        private Bitmap getBitmapFromMemCache(int key) {
            return mMemoryCache.get(key);
        }
    }

}

然后,我将我的基准代码放在这个文件中:https ://github.com/nhaarman/ListViewAnimations/blob/master/library/src/com/haarman/listviewanimations/itemmanipulation/ExpandableListItemAdapter.java#L277

    private ValueAnimator createHeightAnimator(int start, int end) {
        Log.i("CHEOK", start + " -> " + end);
        ValueAnimator animator = ValueAnimator.ofInt(start, end);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int value = (Integer) valueAnimator.getAnimatedValue();

                ViewGroup.LayoutParams layoutParams = mContentParent.getLayoutParams();
                layoutParams.height = value;
                Log.i("CHEOK", "height = " + value);
                mContentParent.setLayoutParams(layoutParams);
            }
        });
        return animator;
    }

这是我得到的结果

// Using DialogFragment
10-12 03:36:05.695: I/CHEOK(29407): height = 0
10-12 03:36:06.258: I/CHEOK(29407): height = 0
10-12 03:36:06.347: I/CHEOK(29407): height = 74
10-12 03:36:06.547: I/CHEOK(29407): height = 358
10-12 03:36:06.621: I/CHEOK(29407): height = 360



// Using Activity
10-12 03:37:02.375: I/CHEOK(29956): height = 0
10-12 03:37:02.387: I/CHEOK(29956): height = 0
10-12 03:37:02.406: I/CHEOK(29956): height = 3
10-12 03:37:02.461: I/CHEOK(29956): height = 51
10-12 03:37:02.476: I/CHEOK(29956): height = 72
10-12 03:37:02.492: I/CHEOK(29956): height = 98
10-12 03:37:02.512: I/CHEOK(29956): height = 131
10-12 03:37:02.531: I/CHEOK(29956): height = 164
10-12 03:37:02.547: I/CHEOK(29956): height = 196
10-12 03:37:02.562: I/CHEOK(29956): height = 228
10-12 03:37:02.582: I/CHEOK(29956): height = 260
10-12 03:37:02.601: I/CHEOK(29956): height = 290
10-12 03:37:02.617: I/CHEOK(29956): height = 312
10-12 03:37:02.633: I/CHEOK(29956): height = 331
10-12 03:37:02.652: I/CHEOK(29956): height = 348
10-12 03:37:02.672: I/CHEOK(29956): height = 357
10-12 03:37:02.687: I/CHEOK(29956): height = 360

有什么想法,为什么会这样?我们如何提高性能DialogFragment

我放置了整个项目文件,以防您有兴趣测试。

https://www.dropbox.com/s/5q6ttn11o92g39n/ListViewAnimations-master.zip

更新

通过使用 TraceView 进行跟踪,我注意到在使用DialogFragment展开ListViewitem 时,ArrayAdapter会调用频繁。我不确定为什么会这样。我在不使用开源库的情况下做了另一个独立的实现。我仍然面临同样的问题。

在 上执行动画DialogFragment,在动画进行时ArrayAdaptergetView频繁触发 。

对于 Activity,当动画正在进行时,不会触发ArrayAdapter's 。getView

使用 DialogFragment 时的 TraceView 在此处输入图像描述

使用 Activity 时的 TraceView 在此处输入图像描述

4

1 回答 1

1

我发现这个问题的关键解决方案是,始终修复对话框大小。一旦我们确定了尺寸,在动画期间将不会花费任何资源来执行尺寸计算。

    final ViewTreeObserver vto = view.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        @SuppressLint("NewApi")
        @SuppressWarnings("deprecation")
        @Override
        public void onGlobalLayout() {
            // Key area to perform optimization. Fix the dialog size!
            // During ListView's rows animation, dialog will never need to 
            // perform computation again.
            int width = dialog.getWindow().getDecorView().getWidth();
            int height = dialog.getWindow().getDecorView().getHeight();
            dialog.getWindow().setLayout(width, height);

            ViewTreeObserver obs = view.getViewTreeObserver();
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
                obs.removeOnGlobalLayoutListener(this);
            } else {
                obs.removeGlobalOnLayoutListener(this);
            }
        }
    });
于 2013-10-12T12:42:27.110 回答