Come animare gli oggetti di RecyclerView quando vengono visualizzati


237

Come posso animare gli oggetti di RecyclerView quando appaiono?

L'animatore elemento predefinito si anima solo quando vengono aggiunti o rimossi dati dopo aver impostato i dati del riciclatore. Sto sviluppando nuove applicazioni e non ho idea di dove iniziare.

Qualche idea su come raggiungere questo obiettivo?

Risposte:


42

Semplificato solo con XML

Visita Gist Link

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>

Utilizzare in layout e recylcerview come:

<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 Modificando il file di animazione. Fare riferimento: stackoverflow.com/questions/5151591/...
iamnaran

Questa è di gran lunga la risposta più pratica.
Oliver Metz,

come farlo funzionare ogni volta che si apre l'elenco, perché ora lo fa solo per la prima volta.
Hiwa Jalal,

7
Devi chiamare recyclerView.scheduleLayoutAnimation()dopo aver modificato il set di dati, altrimenti l'animazione non funzionerebbe.
martedì

Questo dovrebbe funzionare quando gli articoli vengono riciclati e tornano in vista? Ho provato questa soluzione e funziona benissimo per l'animazione iniziale quando puoi vedere il layout per la prima volta. Dopo lo scorrimento, gli elementi non hanno l'animazione quando tornano alla visualizzazione.
Jason p

315

MODIFICARE :

Secondo la documentazione di ItemAnimator :

Questa classe definisce le animazioni che avvengono sugli oggetti quando vengono apportate modifiche all'adattatore.

Quindi, a meno che tu non aggiunga i tuoi articoli uno alla volta al tuo RecyclerViewe aggiorni la vista ad ogni iterazione, non credoItemAnimator sia la soluzione alle tue necessità.

Ecco come è possibile animare gli RecyclerViewelementi quando vengono visualizzati utilizzando un 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 il tuo custom_item_layout sarebbe simile a questo:

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

Per ulteriori informazioni su CustomAdapters e RecyclerView, consultare questo corso di formazione sulla documentazione ufficiale .

Problemi con lo scorrimento veloce

L'uso di questo metodo potrebbe causare problemi con lo scorrimento veloce. La vista potrebbe essere riutilizzata mentre l'animazione è in corso. Al fine di evitare ciò è consigliabile cancellare l'animazione quando è staccato.

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

Su CustomViewHolder:

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

Vecchia risposta:

Dai un'occhiata al repository di Gabriele Mariotti , sono abbastanza sicuro che troverai ciò di cui hai bisogno. Fornisce semplici ItemAnimator per RecyclerView, come SlideInItemAnimator o SlideScaleItemAnimator.


1
L'ho visto, ma è per aggiungere e rimuovere elementi dopo che sono apparsi. Devo iniziare l'animazione prima che appaiano. Grazie comunque Mathieu.
PaulNunezM,

1
Per quanto ne so, è necessario utilizzare un CustomAdapter quindi.
MathieuMaree,

20
Mi chiedevo se hai sperimentato e forse risolto l'effetto "bloccato" con queste animazioni in RecyclerView? Ho usato un codice simile per ListView e i miei oggetti sono stati animati senza problemi indipendentemente dalla velocità con cui ho fatto scorrere, ma con RecyclerView se scorro velocemente alcuni oggetti a volte rimangono bloccati sullo schermo sopra gli altri e non si nascondono del tutto - è come se l'animazione fosse stata fermata prima che finisse. In realtà ho provato a commentare la parte del codice che popola i campi (cercando di accelerare l'esecuzione del metodo onBindViewHolder), quindi ho lasciato il codice solo per animat
Tomislav

4
@MathieuMaree Grazie per l'animazione stupefacente. Sembra buono per lo scorrimento lento, ma sugli oggetti di riciclo rapido dello scorrimento veloce sovrapposti. Hai trovato questo problema? Qualche suggerimento per superare questo problema.
Giru Bhai,

40
@GiruBhai ignora onViewDetachedFromWindowe richiama clearAnimationla vista. Il problema è che ci sono animazioni in esecuzione quando RecyclerView sta cercando di riutilizzare la vista.
Xample

62

Ho animato lo sbiadimento degli Recyclerviewoggetti quando compaiono per la prima volta come mostrato nel codice seguente. Forse questo sarà utile a qualcuno.

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

È inoltre possibile sostituire setFadeAnimation()con quanto segue setScaleAnimation()per animare l'aspetto degli elementi ridimensionandoli da un punto:

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

Il codice sopra ha alcune verruche in quanto quando si scorre gli RecyclerViewoggetti si sbiadiscono o si ridimensionano sempre. Se lo desideri, puoi aggiungere un codice per consentire che l'animazione si verifichi quando il frammento o l'attività che contiene RecyclerViewviene creato per la prima volta (ad esempio, ottieni il tempo di sistema alla creazione e consenti solo l'animazione per i primi FADE_DURATION millisecondi).


1
Ho apportato piccole modifiche alla tua risposta per far funzionare l'animazione solo con lo scorrimento verso il basso, controlla la mia risposta
Basheer AL-MOMANI

1
questo rovina il layout (voci di elenco
sovrascritte

Questo non è il modo corretto o consigliato per animare gli articoli di riciclo. Devi usare ItemAnimator class
Eco4ndly

25

Ho creato l'animazione dalla risposta di pbm con poco modificationper far funzionare l'animazione solo una volta

in altre parole il 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 in onBindViewHolderchiamata la funzione

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

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

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

1
Che cos'è lastPosition qui? È arrayList.size () - 1
Sumit Shukla

1
lastPositionrappresenta il numero di viste renderizzate, quindi l'inizio il suo valore -1, ogni volta che viene visualizzata una nuova vista iniziamo un'animazione e aumentiamo la posizione
Basheer AL-MOMANI

15

È possibile aggiungere un android:layoutAnimation="@anim/rv_item_animation"attributo in RecyclerViewquesto modo:

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

grazie per l'eccellente articolo qui: https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213


Questo ha funzionato per me quando si carica per la prima volta un riciclaggio, ma non quando si aggiungono nuovi elementi. (Anche se immagino che fosse la domanda un ottimo lavoro)
C. Skjerdal,

8

Un buon punto di partenza è questo: https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.java

Non hai nemmeno bisogno della libreria completa, quella classe è sufficiente. Quindi se implementi la tua classe Adapter dando un animatore come questo:

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

vedrai gli elementi apparire dal basso mentre scorrono, evitando anche il problema con lo scorrimento veloce.


3

L'animazione degli oggetti nella vista riciclo quando sono associati nell'adattatore potrebbe non essere la migliore idea in quanto ciò può causare l'animazione degli oggetti nella vista riciclo a velocità diverse. Nel mio caso, l'elemento alla fine della vista di riciclo si animano nella loro posizione più rapidamente di quelli in alto poiché quelli in alto devono spostarsi ulteriormente in modo da renderlo disordinato.

Il codice originale che ho usato per animare ogni elemento nella vista riciclo è disponibile qui:

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

Ma copierò e incollerò il codice in caso di interruzione del collegamento.

PASSAGGIO 1: impostalo all'interno del metodo onCreate in modo da assicurarti che l'animazione venga eseguita una sola volta:

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

PASSAGGIO 2: dovrai inserire questo codice nel metodo in cui desideri avviare l'animazione:

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

Nel link, lo scrittore sta animando le icone della barra degli strumenti, quindi l'ha inserito in questo metodo:

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

PASSAGGIO 3: ora scrivi la logica per 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();
}

La mia alternativa preferita:

Preferirei animare l'intero recyclerview invece degli elementi all'interno del recyclerview.

I PASSI 1 e 2 rimangono gli stessi.

In STEP 3, non appena la tua chiamata API torna con i tuoi dati, vorrei avviare l'animazione.

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

Ciò animerebbe l'intera vista di riciclo in modo che voli dalla parte inferiore dello schermo.


stai parlando di animare l'intero
panorama

Sì - lo sono. Dovresti leggere il primo paragrafo sul motivo per cui penso che l'animazione dell'intero riciclo sia un'idea migliore di ogni articolo. Puoi provare ad animare ogni elemento ma non avrà un bell'aspetto.
Simon,

Che cosa è latestPostRecyclerview?
Antonio,

1
Hai letto il primo paragrafo della mia risposta in cui avevo indicato il motivo per cui animare le visualizzazioni degli articoli non è una grande idea? Inoltre suggerirei di provare la soluzione accettata e poi ti rendi conto del problema, torna indietro e prova questa soluzione.
Simon,

3

Creare questo metodo nell'adattatore recyclerview

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

E infine aggiungi questa riga di codice in onBindViewHolder

setZoomInAnimation(holder.itemView);


2

Nel 2019, suggerirei di inserire tutte le animazioni degli oggetti in ItemAnimator.

Cominciamo con la dichiarazione dell'animatore nella vista riciclatore:

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

Dichiara quindi l'animatore personalizzato,

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

}

Simile a quelli sopra ce n'è uno per la scomparsa animateDisappearance, per l'aggiunta animateAdd, per il cambiamento animateChangee lo spostamento animateMove.

Un punto importante sarebbe quello di chiamare i corretti animatori di animazioni al loro interno.


Potresti fornire un esempio di animazione di aspetto personalizzata usando questa funzione di sostituzione? Non riesco a trovare un esempio e non sono sicuro di dover specificare l'animazione nella funzione.
minar

1
gist.github.com/tadfisher/120d03f8380bfa8a16bf L' ho trovato online su una rapida ricerca, questo mi dà un'idea di come funziona il sovraccarico
Dinesh

0

Estende il tuo adattatore come di seguito

public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder> 

E aggiungi il metodo super a onBindViewHolder

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

È un modo automatico per creare un adattatore animato come "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

Penso che sia meglio usarlo in questo modo: ( nell'adattatore RecyclerView sovrascrivere solo un metodo)

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

    setBindAnimation(holder)
}

Se vuoi ogni animazione allegata lì in RV.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.