【问题标题】:Animate RecyclerView when scrolling滚动时为 RecyclerView 设置动画
【发布时间】:2014-10-12 08:15:30
【问题描述】:

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

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

我对@9​​87654323@ 什么时候调用有点困惑?我在该类中设置了一些断点,但它们都没有停止我的应用程序。

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


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

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

到目前为止,我正在寻找 PERSISTENT、DISAPPEARING 和 APPEARING,但由于这里的这一行,这些方法永远不会被调用:

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

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

【问题讨论】:

  • 您可以使用 api11 中引入的 objectanimtor 对任何对象进行动画处理
  • 很好,但是我怎么知道什么时候需要开始动画呢?

标签: android android-animation android-recyclerview


【解决方案1】:

我最终使用OnScrollListener 并以自定义animate() 方法对其进行动画处理。在我的情况下,该代码只需要 2 毫秒,因此对于 60 fps 来说没问题。

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();
    }
});

【讨论】:

  • 你能添加更多代码吗?比如 scrollToNext ?
  • 这超出了我的问题范围。你应该问一个独立的问题。请记住在实施时显示您当前的进度:)
  • @rekire 你能帮我解决我的问题吗:stackoverflow.com/questions/45858059/…
【解决方案2】:

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

更新: 要修复快速滚动行为,请覆盖适配器的 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>

最后把这段代码放在onBindViewHolderrecyclerView 中。创建一个名为 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;

【讨论】:

  • notifiydatasetchange 时动画再次出现。有什么办法解决吗?
【解决方案3】:

好的解决方案是在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;

这是link

【讨论】:

  • 此解决方案适用于某个库,并非适用于所有人。
猜你喜欢
  • 2016-11-01
  • 1970-01-01
  • 2013-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多