Como animar itens do RecyclerView quando eles aparecem

237

Como posso animar os itens do RecyclerView quando eles estão aparecendo?

O animador de item padrão somente anima quando um dado é adicionado ou removido após a definição dos dados do reciclador. Sou novo desenvolvedor de aplicativos e não tenho idéia por onde começar.

Alguma idéia de como conseguir isso?

PaulNunezM
fonte

Respostas:

42

Simplificado apenas com XML

Visite o link Gist

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>

Use em layouts e recylcerview como:

<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" />
iamnaran
fonte
1
@ArnoldBrown Alterando o arquivo de animação. Consulte: stackoverflow.com/questions/5151591/…
iamnaran
Esta é de longe a resposta mais prática.
Oliver Metz
como fazer isso funcionar toda vez que a lista é aberta, porque agora ela é executada apenas pela primeira vez.
Hiwa Jalal
7
Você deve ligar recyclerView.scheduleLayoutAnimation()após a alteração do conjunto de dados; caso contrário, a animação não funcionaria.
zeleven 18/01
Isso deve funcionar quando os itens são reciclados e voltam à exibição? Tentei esta solução e funciona muito bem na animação inicial quando você pode ver o layout pela primeira vez. Após a rolagem, os itens não possuem a animação ao voltar à exibição.
Jason p
315

EDIT:

De acordo com a documentação do ItemAnimator :

Esta classe define as animações que ocorrem nos itens à medida que são feitas alterações no adaptador.

Portanto, a menos que você adicione seus itens um a um RecyclerViewe atualize a visualização a cada iteração, acho queItemAnimator seja a solução para sua necessidade.

Aqui está como você pode animar os RecyclerViewitens quando eles aparecem usando um 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;
        }
    }
}

E seu custom_item_layout ficaria assim:

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

Para obter mais informações sobre CustomAdapters e RecyclerView, consulte este treinamento na documentação oficial. .

Problemas na rolagem rápida

O uso desse método pode causar problemas na rolagem rápida. A exibição pode ser reutilizada enquanto a animação está acontecendo. Para evitar isso, é recomendável limpar a animação quando ela estiver desanexada.

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

No CustomViewHolder:

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

Resposta antiga:

Dê uma olhada no repo de Gabriele Mariotti , tenho certeza que você encontrará o que precisa. Ele fornece ItemAnimators simples para o RecyclerView, como SlideInItemAnimator ou SlideScaleItemAnimator.

MathieuMaree
fonte
1
Eu já vi isso, mas é para adicionar e remover itens depois que eles aparecerem. Eu preciso iniciar a animação antes que eles apareçam. Obrigado mesmo assim Mathieu.
PaulNunezM
1
Até onde eu sei, você precisa usar um CustomAdapter.
MathieuMaree
20
Eu queria saber se você já experimentou e talvez resolveu o efeito "preso" com essas animações no RecyclerView? Usei código semelhante para o ListView e meus itens foram animados sem problemas, independentemente da velocidade da rolagem, mas com o RecyclerView, se eu rolar rapidamente, alguns itens ficam presos na tela em cima dos outros itens e eles não se ocultam completamente - é como se a animação tivesse sido parada antes de terminar. Na verdade, tentei comentar a parte do código que preenche os campos (tentando acelerar a execução do método onBindViewHolder), por isso só deixei o código para animat.
Tomislav
4
@MathieuMaree Obrigado por esta incrível animação. Parece bom para rolagem lenta, mas os itens de reciclagem de rolagem rápida se sobrepõem.Você achou esse problema? Alguma sugestão para solucionar esse problema?
Giru Bhai 26/02
40
@GiruBhai substitui onViewDetachedFromWindowe chama clearAnimationa visualização. O problema é que existem animações em execução quando o RecyclerView está tentando reutilizar a exibição.
Xample 24/03
62

Eu animei o desvanecimento dos Recyclerviewitens quando eles aparecem pela primeira vez, conforme mostrado no código abaixo. Talvez isso seja útil para alguém.

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

Você também pode substituir setFadeAnimation()o seguinte setScaleAnimation()para animar a aparência dos itens, escalando-os a partir de um ponto:

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

O código acima tem algumas verrugas, na medida em que você rola os RecyclerViewitens sempre desbotando ou escalando. Se desejar, você pode adicionar código para permitir que a animação aconteça quando o fragmento ou a atividade que contém o RecyclerViewprimeiro é criado (por exemplo, obtenha o tempo do sistema na criação e permita apenas a animação nos primeiros milissegundos de FADE_DURATION).

pbm
fonte
1
Fiz pequena modificação para a sua resposta para tornar o trabalho de animação na rolagem para baixo somente, verificar a minha resposta
Basheer AL-Momani
1
este despojos do layout (itens da lista de sobre-escritos, alguns itens que têm a cor do texto errado) em rápida rolagem para cima e para baixo
mrid
Essa não é a maneira adequada ou recomendada para animar itens de revisão de reciclagem. Você deve estar usando a classe
ItemAnimator
25

Criei animações a partir da respostamodification da pbm com pouco para fazer com que a informação funcionasse apenas uma vez

na outra palavra, o 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;
    }
}

e em onBindViewHolderchamar a função

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

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

// call Animation function
setAnimation(holder.itemView, position);            
}
Basheer AL-MOMANI
fonte
1
O que é lastPosition aqui? É arrayList.size () - 1
Sumit Shukla
1
lastPositionrepresenta o número de pontos de vista prestados, por isso o início, o seu valor -1, cada vez que uma nova visão está sendo processado começamos uma animação e aumentar a posição
Basheer AL-Momani
15

Você pode adicionar um android:layoutAnimation="@anim/rv_item_animation"atributo para o RecyclerViewseguinte:

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

obrigado pelo excelente artigo aqui: https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213

Pavel Biryukov
fonte
Isso funcionou para mim quando uma reciclagem foi carregada pela primeira vez, mas não ao adicionar novos itens. (Embora eu ache que essa foi a pergunta, trabalho tão bom)
C. Skjerdal
8

Um bom lugar para começar é este: https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.java

Você nem precisa da biblioteca completa, essa classe é suficiente. Então, se você acabou de implementar sua classe Adapter, fornecendo um animador como este:

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

você verá itens aparecendo na parte inferior à medida que rolam, evitando também o problema com a rolagem rápida.

Alessandro Crugnola
fonte
3

Animar itens na visualização de reciclagem quando estão ligados no adaptador pode não ser a melhor idéia, pois isso pode fazer com que os itens na visualização de reciclagem sejam animados em velocidades diferentes. No meu caso, o item no final da revisão da animação é animado para sua posição mais rapidamente do que os que estão no topo e os que estão no topo ainda precisam viajar, fazendo com que pareça desarrumado.

O código original que usei para animar cada item na revisão de reciclagem pode ser encontrado aqui:

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

Mas vou copiar e colar o código, caso o link seja quebrado.

PASSO 1: Defina isso dentro do método onCreate para garantir que a animação seja executada apenas uma vez:

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

PASSO 2: Você precisará inserir este código no método em que deseja iniciar a animação:

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

No link, o escritor está animando os ícones da barra de ferramentas, então ele o colocou dentro deste método:

@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;
}

PASSO 3: Agora escreva a lógica para o 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();
}

Minha alternativa preferida:

Prefiro animar a visão geral da reciclagem em vez dos itens contidos nela.

Os PASSOS 1 e 2 permanecem os mesmos.

No PASSO 3, assim que sua chamada à API retornar com seus dados, eu iniciaria a animação.

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

Isso animaria toda a sua reciclagem para que ela voasse da parte inferior da tela.

Simon
fonte
você está falando de animar toda a recyclerview
Longerian
Sim eu sou. Você deve ler o primeiro parágrafo sobre por que acho que animar toda a reciclagem é uma ideia melhor do que cada item. Você pode tentar animar cada item, mas ele não ficará bem.
Simon
O que é latestPostRecyclerview?
Antonio
1
Você leu o primeiro parágrafo da minha resposta em que afirmei o motivo pelo qual animar as visualizações de itens não é uma ótima idéia? Também sugiro que tente a solução aceita e, em seguida, você perceba o problema, volte e tente esta solução.
Simon
3

Crie este método no seu adaptador de reciclagem

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

E, finalmente, adicione esta linha de código no onBindViewHolder

setZoomInAnimation(holder.itemView);

Md.Tarikul Islam
fonte
2

Em 2019, eu sugeriria colocar todas as animações de itens no ItemAnimator.

Vamos começar declarando o animador na exibição do reciclador:

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

Declare o animador personalizado,

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

}

Semelhante aos acima, há um para o desaparecimento animateDisappearance, para adicionar animateAdd, para mudar animateChangee moveranimateMove .

Um ponto importante seria chamar os distribuidores de animação corretos dentro deles.

Dinesh
fonte
Você poderia fornecer um exemplo de uma animação de aparência personalizada usando essa função de substituição? Não consigo encontrar um exemplo e não tenho certeza se preciso especificar a animação na função.
minar
1
gist.github.com/tadfisher/120d03f8380bfa8a16bf Encontrei isso online em uma pesquisa rápida, isso dá uma idéia de como a sobrecarga funciona
Dinesh
0

Apenas estende seu adaptador como abaixo

public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder> 

E adicione super método ao onBindViewHolder

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

É uma maneira automatizada de criar um adaptador animado como "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;
        }
    }

}
Eliasz Kubala
fonte
0

Eu acho que é melhor usá-lo assim: (no adaptador RecyclerView substitui apenas um método)

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

    setBindAnimation(holder)
}

Se você quiser todas as animações anexadas no RV.

Milan Jurkulak
fonte