出现时如何为RecyclerView设置动画


237

出现时如何为RecyclerView项设置动画?

默认项目动画器仅在设置了回收者数据之后才添加或删除数据时进行动画处理。我是新开发的应用程序,不知道从哪里开始。

任何想法如何实现这一目标?

Answers:


42

仅通过XML变得简单

访问要点链接

res / anim / layout_animation.xml

<?xml version="1.0" encoding="utf-8"?>
    <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:animation="@anim/item_animation_fall_down"
        android:animationOrder="normal"
        android:delay="15%" />

res / anim / item_animation_fall_down.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="500">

    <translate
        android:fromYDelta="-20%"
        android:toYDelta="0"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

    <scale
        android:fromXScale="105%"
        android:fromYScale="105%"
        android:toXScale="100%"
        android:toYScale="100%"
        android:pivotX="50%"
        android:pivotY="50%"
        android:interpolator="@android:anim/decelerate_interpolator"
        />

</set>

在布局和recylcerview中使用,例如:

<android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layoutAnimation="@anim/layout_animation"
                app:layout_behavior="@string/appbar_scrolling_view_behavior" />

1
@ArnoldBrown通过更改动画文件。请参阅:stackoverflow.com/questions/5151591/…–
iamnaran,

到目前为止,这是最实际的答案。
奥利弗·梅斯

每次打开列表时如何使它起作用,因为现在它仅是第一次。
Hiwa Jalal,

7
您必须recyclerView.scheduleLayoutAnimation()在数据集更改后调用,否则,动画将无法工作。
zeleven

这在回收物品并重新出现时应该有用吗?我尝试了这种解决方案,当您第一次看到布局时,它非常适合初始动画。滚动后,项目重新显示时没有动画。
Jason p

315

编辑:

根据ItemAnimator文档

此类定义在对适配器进行更改时在项目上发生的动画。

因此,除非您逐个添加项目RecyclerView并在每次迭代时刷新视图,否则我认为这不是ItemAnimator您需要的解决方案。

您可以RecyclerView使用以下方法使用CustomAdapter 为项目显示动画:

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder>
{
    private Context context;

    // The items to display in your RecyclerView
    private ArrayList<String> items;
    // Allows to remember the last item shown on screen
    private int lastPosition = -1;

    public static class ViewHolder extends RecyclerView.ViewHolder
    {
        TextView text;
        // You need to retrieve the container (ie the root ViewGroup from your custom_item_layout)
        // It's the view that will be animated
        FrameLayout container;

        public ViewHolder(View itemView)
        {
            super(itemView);
            container = (FrameLayout) itemView.findViewById(R.id.item_layout_container);
            text = (TextView) itemView.findViewById(R.id.item_layout_text);
        }
    }

    public CustomAdapter(ArrayList<String> items, Context context)
    {
        this.items = items;
        this.context = context;
    }

    @Override
    public CustomAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_item_layout, parent, false);
        return new ViewHolder(v);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position)
    {
        holder.text.setText(items.get(position));

        // Here you apply the animation when the view is bound
        setAnimation(holder.itemView, position);
    }

    /**
     * Here is the key method to apply the animation
     */
    private void setAnimation(View viewToAnimate, int position)
    {
        // If the bound view wasn't previously displayed on screen, it's animated
        if (position > lastPosition)
        {
            Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
            viewToAnimate.startAnimation(animation);
            lastPosition = position;
        }
    }
}

您的custom_item_layout看起来像这样:

<FrameLayout
    android:id="@+id/item_layout_container"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/item_layout_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:gravity="center_vertical"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"/>

</FrameLayout>

有关CustomAdapters的更多信息RecyclerView,请参考官方文档上的培训

快速滚动问题

使用此方法可能会导致快速滚动出现问题。动画发生时可以重用该视图。为了避免这种情况,建议在分离动画时清除动画。

    @Override
    public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder)
    {
        ((CustomViewHolder)holder).clearAnimation();
    }

在CustomViewHolder上:

    public void clearAnimation()
    {
        mRootLayout.clearAnimation();
    }

旧答案:

看一下Gabriele Mariotti的仓库,我很确定你会找到你需要的。他为RecyclerView提供了简单的ItemAnimator,例如SlideInItemAnimator或SlideScaleItemAnimator。


1
我已经看到了,但这是为了在项目出现后添加和删除它们。我需要在它们出现之前立即开始动画。无论如何,谢谢Mathieu。
PaulNunezM 2014年

1
据我所知,那么您需要使用CustomAdapter。
MathieuMaree 2014年

20
我想知道您是否已经体验过RecyclerView中的这些动画,并解决了这种“卡死”效果?我为ListView使用了类似的代码,无论滚动多快,我的项目都没有动画问题,但是如果使用RecyclerView,如果我快速滚动,某些项目有时会卡在其他项目的屏幕上,而它们不会完全隐藏-就像动画在完成之前已停止。我实际上试图注释掉填充字段的代码部分(试图加快onBindViewHolder方法的执行),所以我只将代码留给了animat
Tomislav

4
@MathieuMaree感谢您的神奇动画。它对于慢速滚动看起来不错,但是在快速滚动recyclerview项上重叠。您发现这个问题了吗?有任何解决此问题的建议。
Giru Bhai 2015年

40
@GiruBhai覆盖onViewDetachedFromWindow并调用clearAnimation视图。问题是当RecyclerView尝试重用视图时,正在运行动画。
Xample'3

62

Recyclerview当项目首次出现时,我对它们的淡入效果进行了动画处理,如下面的代码所示。也许这对某人有用。

private final static int FADE_DURATION = 1000; //FADE_DURATION in milliseconds

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    holder.getTextView().setText("some text");

    // Set the view to fade in
    setFadeAnimation(holder.itemView);            
}

private void setFadeAnimation(View view) {
    AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f);
    anim.setDuration(FADE_DURATION);
    view.startAnimation(anim);
}

您还可以替换setFadeAnimation()为以下内容,setScaleAnimation()以从一个点缩放它们来动画化它们的外观:

private void setScaleAnimation(View view) {
    ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
    anim.setDuration(FADE_DURATION);
    view.startAnimation(anim);
}

上面的代码在滚动RecyclerView项目时总是有些淡淡或缩放。如果您愿意,可以添加代码以仅在RecyclerView第一次创建包含的片段或活动时允许动画发生(例如,获得创建时的系统时间,并且仅允许前FADE_DURATION毫秒内的动画)。


1
我对您的答案做了一些小修改,以使动画只能向下滚动,请检查我的答案
Basheer AL-MOMANI 2016年

1
这会在向上和向下快速滚动时破坏布局(覆盖的列表项目,某些项目的文本颜色错误)
闷闷的

这不是为recyclerview项目制作动画的正确或建议的方法。您必须使用ItemAnimator类
Eco4ndly,

25

我用pbm的答案创建了动画,几乎没有modification使动画运行一次

换句话说 Animation appear with you scroll down only

private int lastPosition = -1;

private void setAnimation(View viewToAnimate, int position) {
    // If the bound view wasn't previously displayed on screen, it's animated
    if (position > lastPosition) {
        ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
        viewToAnimate.startAnimation(anim);
        lastPosition = position;
    }
}

并在onBindViewHolder调用函数

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

holder.getTextView().setText("some text");

// call Animation function
setAnimation(holder.itemView, position);            
}

1
这里的lastPosition是什么?是否为arrayList.size()-1
Sumit Shukla,

1
lastPosition表示已渲染视图的数量,因此它是其值的开始-1,每次渲染新视图时,我们都会启动动画并增加位置
Basheer AL-MOMANI 18-4-4

15

您可以添加如下android:layoutAnimation="@anim/rv_item_animation"属性RecyclerView

<android.support.v7.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="match_parent"                                        
    android:layoutAnimation="@anim/layout_animation_fall_down"
    />

感谢此处的出色文章:https : //proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213


这在第一次加载recyclerview时对我有用,但在添加新项目时不起作用。(尽管我猜这是一个很好的问题)
C. Skjerdal

8

一个不错的起点是:https : //github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.java

您甚至不需要完整的库,该类就足够了。然后,如果您只是实现Adapter类,从而提供一个动画器,如下所示:

@Override
protected Animator[] getAnimators(View view) {
    return new Animator[]{
            ObjectAnimator.ofFloat(view, "translationY", view.getMeasuredHeight(), 0)
    };
}

@Override
public long getItemId(final int position) {
    return getWrappedAdapter().getItemId(position);
}

您会在滚动项目时从底部看到它们,这也避免了快速滚动的问题。


3

当将recyclerview中的项目绑定到适配器中时,对其进行动画处理可能不是最好的主意,因为这可能导致recyclerview中的项目以不同的速度进行动画处理。在我的情况下,recyclerview末端的项目会比顶部的动画更快地动画到其位置,因为顶部的动画需要进一步移动,因此看起来不整齐。

我用来为每个项目添加动画到recyclerview中的原始代码可以在这里找到:

http://frogermcs.github.io/Instagram-with-Material-Design-concept-is-getting-real/

但是,如果链接断开,我将复制并粘贴代码。

步骤1:在onCreate方法中进行设置,以确保动画仅运行一次:

if (savedInstanceState == null) {
    pendingIntroAnimation = true;
}

步骤2:您需要将此代码放入要开始动画的方法中:

if (pendingIntroAnimation) {
    pendingIntroAnimation = false;
    startIntroAnimation();
}

在链接中,作者为工具栏图标设置了动画,因此他将其放在此方法中:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu_main, menu);
    inboxMenuItem = menu.findItem(R.id.action_inbox);
    inboxMenuItem.setActionView(R.layout.menu_item_view);
    if (pendingIntroAnimation) {
        pendingIntroAnimation = false;
        startIntroAnimation();
    }
    return true;
}

步骤3:现在为startIntroAnimation()编写逻辑:

private static final int ANIM_DURATION_TOOLBAR = 300;

private void startIntroAnimation() {
    btnCreate.setTranslationY(2 * getResources().getDimensionPixelOffset(R.dimen.btn_fab_size));

    int actionbarSize = Utils.dpToPx(56);
    toolbar.setTranslationY(-actionbarSize);
    ivLogo.setTranslationY(-actionbarSize);
    inboxMenuItem.getActionView().setTranslationY(-actionbarSize);

    toolbar.animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(300);
    ivLogo.animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(400);
    inboxMenuItem.getActionView().animate()
            .translationY(0)
            .setDuration(ANIM_DURATION_TOOLBAR)
            .setStartDelay(500)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    startContentAnimation();
                }
            })
            .start();
}

我的首选替代方法:

我宁愿为整个recyclerview而不是recyclerview中的项目设置动画。

步骤1和2保持不变。

在STEP 3中,只要您的API调用返回了您的数据,我就开始动画。

private void startIntroAnimation() {
    recyclerview.setTranslationY(latestPostRecyclerview.getHeight());
    recyclerview.setAlpha(0f);
    recyclerview.animate()
            .translationY(0)
            .setDuration(400)
            .alpha(1f)
            .setInterpolator(new AccelerateDecelerateInterpolator())
            .start();
}

这将使整个recyclerview动画化,以便它从屏幕底部飞入。


您正在谈论使整个recyclerview动画化
Longerian

是的-我是。您应该阅读第一段,说明为什么我认为对整个recyclerview进行动画处理比对每个项目都更好。您可以尝试为每个项目设置动画,但效果会不佳。
西蒙

什么latestPostRecyclerview
安东尼奥

1
您是否在我回答的第一段中读到了我说过的动画项目视图不是一个好主意的原因?我也建议您尝试接受的解决方案,然后您会发现问题,请返回并尝试该解决方案。
西蒙(Simon)

3

在您的recyclerview适配器中创建此方法

private void setZoomInAnimation(View view) {
        Animation zoomIn = AnimationUtils.loadAnimation(context, R.anim.zoomin);// animation file 
        view.startAnimation(zoomIn);
    }

最后在onBindViewHolder中添加这一行代码

setZoomInAnimation(holder.itemView);


2

在2019年,我建议将所有项目动画放入ItemAnimator中。

让我们从在recycler-view中声明动画师开始:

with(view.recycler_view) {
adapter = Adapter()
itemAnimator = CustomAnimator()
}

然后声明自定义动画师,

class CustomAnimator() : DefaultItemAnimator() {

     override fun animateAppearance(
       holder: RecyclerView.ViewHolder,
       preInfo: ItemHolderInfo?,
       postInfo: ItemHolderInfo): Boolean{} // declare  what happens when a item appears on the recycler view

     override fun animatePersistence(
       holder: RecyclerView.ViewHolder,
       preInfo: ItemHolderInfo,
       postInfo: ItemHolderInfo): Boolean {} // declare animation for items that persist in a recycler view even when the items change

}

与上面的类似,有一个消失animateDisappearance,增加animateAdd,改变animateChange和移动animateMove

重要的一点是在其中调用正确的动画分派器。


您能否提供使用此覆盖功能的自定义外观动画的示例?我找不到一个例子,我不确定是否只需要在函数中指定动画。
高塔

1
gist.github.com/tadfisher/120d03f8380bfa8a16bf我在网上进行了快速搜索,发现了超载的工作原理
Dinesh

0

像下面那样扩展您的适配器

public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder> 

并将超级方法添加到onBindViewHolder

@Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        super.onBindViewHolder(holder, position);

这是创建“ Basheer AL-MOMANI”等动画适配器的自动化方法

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;

import java.util.Random;

/**
 * Created by eliaszkubala on 24.02.2017.
 */
public class AnimatedRecyclerView<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {


    @Override
    public T onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(T holder, int position) {
        setAnimation(holder.itemView, position);
    }

    @Override
    public int getItemCount() {
        return 0;
    }

    protected int mLastPosition = -1;

    protected void setAnimation(View viewToAnimate, int position) {
        if (position > mLastPosition) {
            ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            anim.setDuration(new Random().nextInt(501));//to make duration random number between [0,501)
            viewToAnimate.startAnimation(anim);
            mLastPosition = position;
        }
    }

}

0

我认为,最好像这样使用它:(在RecyclerView 适配器中重写只是一种方法)

override fun onViewAttachedToWindow(holder: ViewHolder) {
    super.onViewAttachedToWindow(holder)

    setBindAnimation(holder)
}

如果您想在RV中找到每个附加动画。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.