25

当我滚动 RecyclerView 时,有什么方法可以为它的元素设置动画吗?

我看了一下DefaultItemAnimatorand RecyclerView.ItemAnimator,但动画似乎只有在数据集发生变化时才会被调用,如果我错了,请纠正我。

我有点困惑RecyclerView.ItemAnimator.animateMove()它什么时候被调用?我在该类中放置了一些断点,但它们都没有停止我的应用程序。

但是回到我的问题,如何为 RecyclerView 设置动画?我希望某些元素具有另一种不透明度,这取决于一些自定义规则。


我做了一些更深入的研究,似乎动画移动正是我正在寻找的。该方法从dispatchLayout(). 这是该方法的javadoc:

layoutChildren() 的包装器,用于处理由布局引起的动画更改。动画的工作假设有五种不同的项目在运行:
PERSISTENT:项目在布局之前和之后可见
REMOVED:项目在布局之前可见并且被应用程序删除
ADDED:项目在布局之前不存在并且被添加应用
消失:项目存在于数据集中之前/之后,但在布局过程中从可见变为不可见(它们被移出屏幕作为其他更改的副作用)
出现:项目存在于数据集中之前/之后,但在布局过程中从不可见变为可见(它们在屏幕上移动作为其他更改的副作用)
总体方法确定布局之前/之后存在哪些项目,并为每个项目推断上述五种状态之一。然后相应地设置动画:
PERSISTENT 视图被移动 ({@link ItemAnimator#animateMove(ViewHolder, int, int, int, int)}) REMOVED 视图被移除 ({@link ItemAnimator#animateRemove(ViewHolder)})
ADDED 视图被添加 ({@link ItemAnimator#animateAdd(ViewHolder)})
消失的视图被移出屏幕
出现的视图被移到屏幕上

到目前为止,我正在寻找持久性、消失和出现,但是由于这里的这一行,这些方法从未被调用:

boolean animateChangesSimple = mItemAnimator != null && mItemsAddedOrRemoved
            && !mItemsChanged;

mItemsAddedOrRemoved永远是假的,所以没有一个回调到达。知道如何正确设置设置标志吗?

4

3 回答 3

10

我最终使用了一个自定义方法OnScrollListener并为其设置了动画。animate()在我的情况下,该代码只需要 2ms,因此对于 60fps 来说没有问题。

recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(int newState) {
        if(newState == RecyclerView.SCROLL_STATE_IDLE) {
            // special handler to avoid displaying half elements
            scrollToNext();
        }
        animate();
    }

    @Override
    public void onScrolled(int dx, int dy) {
        animate();
    }
});
于 2014-08-25T07:39:22.557 回答
6

我是这样做的。可能会帮助某人。我不知道这是否是最好的方法,但对我来说效果很好。

更新: 要修复快速滚动行为,请覆盖onViewDetachedFromWindow适配器的方法并调用clearAnimation动画视图(在本例中为holder.itemView.clearAnimation())。

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_top.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>

最后把这段代码放在onBindViewHolder. recyclerView创建一个名为 lastPosition 的字段并将其初始化为 -1。

Animation animation = AnimationUtils.loadAnimation(context,
            (position > lastPosition) ? R.anim.up_from_bottom
                    : R.anim.down_from_top);
    holder.itemView.startAnimation(animation);
    lastPosition = position;
于 2015-04-18T07:03:15.973 回答
0

好的解决方案是在方法中为适配器中的支架设置动画onBindViewHolder。摘自 Material Test 项目 Slidenerd 的片段:

@Override 
public void onBindViewHolder(ViewHolderBoxOffice holder, int position) {
    Movie currentMovie = mListMovies.get(position);
    //one or more fields of the Movie object may be null since they are fetched from the web 
    holder.movieTitle.setText(currentMovie.getTitle());


    //retrieved date may be null 
    Date movieReleaseDate = currentMovie.getReleaseDateTheater();
    if (movieReleaseDate != null) {
        String formattedDate = mFormatter.format(movieReleaseDate);
        holder.movieReleaseDate.setText(formattedDate);
    } else { 
        holder.movieReleaseDate.setText(Constants.NA);
    } 


    int audienceScore = currentMovie.getAudienceScore();
    if (audienceScore == -1) {
        holder.movieAudienceScore.setRating(0.0F);
        holder.movieAudienceScore.setAlpha(0.5F);
    } else { 
        holder.movieAudienceScore.setRating(audienceScore / 20.0F);
        holder.movieAudienceScore.setAlpha(1.0F);
    } 


    if (position > mPreviousPosition) {
        AnimationUtils.animateSunblind(holder, true);         
    } else { 
        AnimationUtils.animateSunblind(holder, false);

    } 
    mPreviousPosition = position;

这是一个链接

于 2015-11-27T23:01:52.460 回答