在 ListView 中对列表项进行动画处理

10
我尝试给我的ListView中的新项目添加动画效果。我有稳定的ID,因此我知道要对哪个元素进行动画处理。问题出现在ListView的回收机制上。当我确定最近插入了一个元素时,我会调用View的startAnimation方法。但是,然后视图被回收,并填充不同的数据。这导致UI动画显示错误的行。某个时刻,视图保存了正确的数据,但随后被回收了。我通过logcat确认了这一点。
是否有任何方法可以解决这个问题?
public ExpensCursorAdapter(Context context, Cursor c, boolean autoRequery,
        CopyOnWriteArraySet<String> fadeAnimateTags) {
    super(context, c, autoRequery);
    this.mFadeAnimTags = fadeAnimateTags;
}

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

@Override
public void bindView(View view, Context context, Cursor cursor) {
    setup(view, context, cursor);
}

private void setup(View view, Context context, Cursor cursor) {
    final String id = cursor.getString(4);
    if (LOCAL_LOGV) Log.v(TAG, String.format("Create item for %s. Received view: %s", id, view.toString()));
    view.setTag(id);
    final TextView dateText = (TextView) view.findViewById(R.id.date);
    final TextView timeText = (TextView) view.findViewById(R.id.time);
    final TextView title = (TextView) view.findViewById(R.id.title);
    final TextView amount = (TextView) view.findViewById(R.id.amount);
    final Date date = new Date(cursor.getLong(0));
    title.setText(cursor.getString(1));
    dateText.setText(dFormat.format(date));
    timeText.setText(tFormat.format(date));
    amount.setText(String.format("%d Ft", cursor.getInt(2)));
    if (cursor.getInt(3) == 1) {
        timeText.setTextColor(Color.LTGRAY);
        title.setTextColor(Color.LTGRAY);
        dateText.setTextColor(Color.LTGRAY);
        amount.setTextColor(Color.LTGRAY);
    } else {
        timeText.setTextColor(Color.BLACK);
        title.setTextColor(Color.BLACK);
        dateText.setTextColor(Color.BLACK);
        amount.setTextColor(Color.BLACK);
    }
    if (mFadeAnimTags.contains(id)) {
     view.setAnimation(AnimationUtils.loadAnimation(context, R.anim.fade));
     mFadeAnimTags.remove(id);
    }

}

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
    final LayoutInflater inflater = LayoutInflater.from(context);
    View view = inflater.inflate(R.layout.expense_list_item, parent, false);
    setup(view, context, cursor);
    return view;
}

你在哪里开始动画?我认为应该在适配器的getView()方法中开始。 - manelizzard
我从那里开始。代码已添加到问题中。 - Mate Gulyas
你尝试过每次“设置”行时创建新的动画对象吗?也许是因为你正在使用静态方法,它总是引用相同的动画,并且从一行变为另一行。 - manelizzard
每次我都会创建一个新的动画。如果它是相同的,那么在第一个之后我就看不到任何东西了。但事实并非如此。问题在于由于视图回收,为动画设置的视图保存了不同的数据。 - Mate Gulyas
@gmate 请接受哪个答案帮助了您,或者发布您自己的答案并接受它。 - Ranjithkumar
显示剩余4条评论
4个回答

8

如果您想使用来自http://kylewbanks.com/blog/Implementing-Google-Plus-Style-ListView-Animations-on-Android的简单代码getView(), (无需自定义库)

private int lastPosition = -1;

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    //Load your view, populate it, etc...
    View view = ...;

    Animation animation = AnimationUtils.loadAnimation(getContext(), (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
    view.startAnimation(animation);
    lastPosition = position;

    return view;
}

up_from_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
        android:fromXDelta="0%" android:toXDelta="0%"
        android:fromYDelta="100%" android:toYDelta="0%"
        android:duration="400" />
</set>

down_from_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
        android:fromXDelta="0%" android:toXDelta="0%"
        android:fromYDelta="-100%" android:toYDelta="0%"
        android:duration="400" />
</set>

5
在自定义适配器的getView方法中为每个添加的元素添加动画效果。
public View getView(int position, View convertView, ViewGroup parent) {

    View v = convertView;

    if (v == null) {
        LayoutInflater vi = (LayoutInflater) getActivity()
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = vi.inflate(R.layout.simple_list_item_1, null);
    }

    ListData o = list.get(position);
    TextView tt = (TextView) v.findViewById(R.id.toptext);

    tt.setText(o.content);

    Log.d("ListTest", "Position : "+position);

    if(flag == false) {
        Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.slide_top_to_bottom);
        v.startAnimation(animation);
    }
    return v;
}

从而实现动画效果。


2
永远不要在适配器中进行动画。 - Korniltsev Anatoly
10
@KorniltsevAnatoly 那么在哪里,怎么做? - Jorge Gil

3
请查看ListViewAnimations库,以为您的ListView项添加动画效果。
基本上,您只需要添加两行代码即可使您的项具有动画效果:
之前:
MyListAdapter mAdapter = new MyListAdapter(this, getItems());
getListView().setAdapter(mAdapter);

之后:

MyListAdapter mAdapter = new MyListAdapter(this, getItems());
AlphaInAnimationAdapter alphaInAnimationAdapter = new AlphaInAnimationAdapter(mAdapter);
alphaInAnimationAdapter.setAbsListView(getListView());
getListView().setAdapter(alphaInAnimationAdapter);

1
+1 真是个很好的库。有一个小问题,一旦加载了listview,我需要向其中添加额外的项目。当添加这些额外的项目时,我希望它们能够以动画的形式显示(仅对新添加的项目进行动画效果),这个库支持吗? - Yasitha Waduge

1

我有一个类似的问题。我不确定这是否是正确的解决方法,但你可以尝试在开始新动画之前清除视图上的动画,以取消从回收视图中留下的任何动画。这对我有帮助。

if(flag == false) {
    v.clearAnimation();
    Animation animation = AnimationUtils.loadAnimation(getActivity(), R.anim.slide_top_to_bottom);
    v.startAnimation(animation);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接