最近,我发现ListView
展开{/ 1}}展开/折叠动画的速度比DialogFragment
慢得多。
在Activity
中,动画FPS为10 fps。每个动画需要100毫秒。
在DialogFragment
中,动画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上进行了修改,以测试Activity
和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");
}
我创建了自己的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);
}
}
}
这是我得到的结果
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
的效果?
我放置整个项目文件,以防你有兴趣测试。
https://www.dropbox.com/s/5q6ttn11o92g39n/ListViewAnimations-master.zip
通过使用TraceView进行跟踪,我注意到当使用DialogFragment
扩展DialogFragment
项时,ListView
将被频繁调用。我不确定为什么会如此。我在不使用开源库的情况下执行另一个独立的实现。我仍然面临同样的问题。
在ArrayAdapter
上执行动画,DialogFragment
的{{1}}将在动画进行过程中频繁触发。
对于活动,当动画正在进行时,ArrayAdapter
的{{1}}将不会被触发。
使用DialogFragment时的TraceView
使用活动时的TraceView
答案 0 :(得分: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);
}
}
});