So animieren Sie RecyclerView-Elemente, wenn sie angezeigt werden

237

Wie kann ich die RecyclerView-Elemente animieren, wenn sie angezeigt werden?

Der Standardelementanimator wird nur animiert, wenn Daten hinzugefügt oder entfernt werden, nachdem die Recyclerdaten festgelegt wurden. Ich bin neu in der Entwicklung von Anwendungen und habe keine Ahnung, wo ich anfangen soll.

Irgendwelche Ideen, wie dies erreicht werden kann?

PaulNunezM
quelle

Antworten:

42

Nur mit XML einfach gemacht

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

Verwendung in Layouts und Recylcerview wie:

<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
quelle
1
@ArnoldBrown Durch Ändern der Animationsdatei. Siehe: stackoverflow.com/questions/5151591/…
iamnaran
Dies ist bei weitem die praktischste Antwort.
Oliver Metz
Wie funktioniert das jedes Mal, wenn die Liste geöffnet wird? Jetzt nur noch zum ersten Mal.
Hiwa Jalal
7
Sie müssen recyclerView.scheduleLayoutAnimation()nach Änderung des Datensatzes aufrufen. Andernfalls funktioniert die Animation nicht.
Zeleven
Sollte dies funktionieren, wenn Gegenstände recycelt werden und wieder in Sicht kommen? Ich habe diese Lösung versucht und sie funktioniert hervorragend für die erste Animation, wenn Sie das Layout zum ersten Mal sehen können. Nach dem Scrollen haben Elemente keine Animation, wenn sie wieder angezeigt werden.
Jason p
314

EDIT:

Gemäß der ItemAnimator-Dokumentation :

Diese Klasse definiert die Animationen, die für Elemente stattfinden, wenn Änderungen am Adapter vorgenommen werden.

Wenn Sie also Ihre Elemente nicht einzeln zu Ihrer hinzufügen RecyclerViewund die Ansicht bei jeder Iteration aktualisieren, ist dies meiner Meinung nach nicht ItemAnimatordie Lösung für Ihre Anforderungen.

So können Sie die RecyclerViewElemente animieren , wenn sie mithilfe eines benutzerdefinierten Adapters angezeigt werden:

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

Und Ihr custom_item_layout würde folgendermaßen aussehen:

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

Weitere Informationen zu CustomAdapters und RecyclerViewfinden Sie in dieser Schulung in der offiziellen Dokumentation .

Probleme beim schnellen Scrollen

Die Verwendung dieser Methode kann zu Problemen beim schnellen Scrollen führen. Die Ansicht kann wiederverwendet werden, während die Animation ausgeführt wird. Um dies zu vermeiden, ist es empfehlenswert, die Animation zu löschen, wenn sie getrennt ist.

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

Auf CustomViewHolder:

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

Alte Antwort:

Schauen Sie sich Gabriele Mariottis Repo an , ich bin mir ziemlich sicher, dass Sie finden werden, was Sie brauchen. Er stellt einfache ItemAnimators für die RecyclerView bereit, z. B. SlideInItemAnimator oder SlideScaleItemAnimator.

MathieuMaree
quelle
1
Ich habe das gesehen, aber das dient zum Hinzufügen und Entfernen von Elementen, nachdem sie angezeigt wurden. Ich muss die Animation direkt starten, bevor sie angezeigt wird. Trotzdem danke Mathieu.
PaulNunezM
1
Soweit ich weiß, müssen Sie dann einen CustomAdapter verwenden.
MathieuMaree
20
Ich habe mich gefragt, ob Sie den "steckengebliebenen" Effekt mit diesen Animationen in RecyclerView erlebt und vielleicht gelöst haben. Ich habe ähnlichen Code für die ListView verwendet und meine Elemente wurden problemlos animiert, egal wie schnell ich gescrollt habe. Wenn ich jedoch mit RecyclerView schnell scrolle, bleiben einige Elemente manchmal auf dem Bildschirm über den anderen Elementen hängen und werden nicht vollständig ausgeblendet - Es ist, als ob die Animation gestoppt wurde, bevor sie beendet wurde. Ich habe tatsächlich versucht, den Teil des Codes zu kommentieren, der die Felder füllt (um die Ausführung der onBindViewHolder-Methode zu beschleunigen), also habe ich den Code nur für animat
Tomislav
4
@MathieuMaree Vielen Dank für diese erstaunliche Animation. Sie sieht gut aus für langsames Scrollen, aber beim schnellen Scrollen überlappen sich die Elemente der Recycling-Ansicht. Haben Sie dieses Problem gefunden? Irgendwelche Vorschläge, um dieses Problem zu beheben.
Giru Bhai
40
@ GiruBhai überschreiben onViewDetachedFromWindowund clearAnimationdie Ansicht aufrufen . Das Problem ist, dass Animationen ausgeführt werden, wenn RecyclerView versucht, die Ansicht wiederzuverwenden.
Beispiel
62

Ich habe das Einblenden von RecyclerviewElementen animiert , wenn sie zum ersten Mal angezeigt werden, wie im folgenden Code gezeigt. Vielleicht ist dies für jemanden von Nutzen.

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

Sie können auch setFadeAnimation()Folgendes ersetzen setScaleAnimation(), um das Erscheinungsbild von Elementen zu animieren, indem Sie sie von einem Punkt aus skalieren:

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

Der obige Code enthält einige Warzen, sofern beim Scrollen die RecyclerViewElemente immer ausgeblendet oder skaliert werden. Wenn Sie möchten, können Sie Code hinzufügen, damit die Animation nur ausgeführt wird, wenn das Fragment oder die Aktivität, die das enthält, zum RecyclerViewersten Mal erstellt wird (z. B. die Systemzeit bei der Erstellung abrufen und die Animation nur für die ersten Millisekunden FADE_DURATION zulassen).

pbm
quelle
1
Ich habe kleine Änderungen an Ihrer Antwort vorgenommen, damit die Animation nur beim
Scrollen
1
Dies beeinträchtigt das Layout (
überschriebene
Dies ist nicht die richtige oder empfohlene Methode zum Animieren von Recycling-Ansichtselementen. Sie müssen die ItemAnimator-Klasse verwenden
Eco4ndly
25

Ich habe eine Animation aus der Antwort von pbm mit wenig erstellt modification, damit die Aninmation nur einmal ausgeführt wird

mit anderen Worten die 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;
    }
}

und onBindViewHolderrufen Sie die Funktion auf

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

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

// call Animation function
setAnimation(holder.itemView, position);            
}
Basheer AL-MOMANI
quelle
1
Was ist lastPosition hier? Ist es arrayList.size () - 1
Sumit Shukla
1
lastPositionstellt die Anzahl der gerenderten Ansichten dar, so dass es der Anfang seines Wertes -1ist. Jedes Mal, wenn eine neue Ansicht gerendert wird, starten wir eine Animation und erhöhen die Position
Basheer AL-MOMANI
15

Sie können ein android:layoutAnimation="@anim/rv_item_animation"Attribut hinzufügen, um Folgendes zu RecyclerViewmögen:

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

Vielen Dank für den ausgezeichneten Artikel hier: https://proandroiddev.com/enter-animation-using-recyclerview-and-layoutanimation-part-1-list-75a874a5d213

Pavel Biryukov
quelle
Dies funktionierte bei mir, wenn eine Recycling-Ansicht zum ersten Mal geladen wurde, jedoch nicht beim Hinzufügen neuer Elemente. (Obwohl ich denke, das war die Frage, so gute Arbeit)
C. Skjerdal
8

Ein guter Anfang ist folgender: https://github.com/wasabeef/recyclerview-animators/blob/master/animators/src/main/java/jp/wasabeef/recyclerview/adapters/AnimationAdapter.java

Sie brauchen nicht einmal die vollständige Bibliothek, diese Klasse ist genug. Wenn Sie dann nur Ihre Adapterklasse implementieren und einen Animator wie diesen angeben:

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

Beim Scrollen werden Elemente von unten angezeigt, wodurch das Problem mit dem schnellen Scrollen vermieden wird.

Alessandro Crugnola
quelle
3

Das Animieren von Elementen in der Recyclingansicht, wenn sie im Adapter gebunden sind, ist möglicherweise nicht die beste Idee, da dies dazu führen kann, dass die Elemente in der Recyclingansicht mit unterschiedlicher Geschwindigkeit animiert werden. In meinem Fall wird der Artikel am Ende der Recycling-Ansicht schneller zu seiner Position animiert als die oben, da sich die oben befindlichen weiter bewegen müssen, sodass er unordentlich aussieht.

Den Originalcode, mit dem ich jeden Artikel in der Recyclingansicht animiert habe, finden Sie hier:

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

Aber ich werde den Code kopieren und einfügen, falls der Link unterbrochen wird.

SCHRITT 1: Stellen Sie dies in Ihrer onCreate-Methode so ein, dass Sie sicherstellen, dass die Animation nur einmal ausgeführt wird:

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

SCHRITT 2: Sie müssen diesen Code in die Methode einfügen, mit der Sie die Animation starten möchten:

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

In dem Link animiert der Autor die Symbolleistensymbole, also fügt er sie in diese Methode ein:

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

SCHRITT 3: Schreiben Sie nun die Logik für 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();
}

Meine bevorzugte Alternative:

Ich würde lieber die gesamte Recyclingansicht animieren als die Elemente in der Recyclingansicht.

SCHRITT 1 und 2 bleiben gleich.

In SCHRITT 3 würde ich die Animation starten, sobald Ihr API-Aufruf mit Ihren Daten zurückkehrt.

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

Dies würde Ihre gesamte Recycling-Ansicht so animieren, dass sie vom unteren Bildschirmrand einfliegt.

Simon
quelle
Sie sprechen über die Animation der gesamten Recycling-
Ansicht
Ja ich bin. Sie sollten den ersten Absatz darüber lesen, warum ich es für eine bessere Idee halte, die gesamte Recycling-Ansicht zu animieren als jedes Element. Sie können versuchen, jedes Element zu animieren, aber es sieht nicht gut aus.
Simon
Was ist latestPostRecyclerview?
Antonio
1
Hast du den ersten Absatz meiner Antwort gelesen, in dem ich den Grund angegeben habe, warum das Animieren von Elementansichten keine so gute Idee ist? Außerdem würde ich vorschlagen, die akzeptierte Lösung auszuprobieren, und dann erkennen Sie das Problem, kommen Sie zurück und probieren Sie diese Lösung aus.
Simon
3

Erstellen Sie diese Methode in Ihrem recyclerview Adapter

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

Und schließlich fügen Sie diese Codezeile in onBindViewHolder hinzu

setZoomInAnimation(holder.itemView);

Md.Tarikul Islam
quelle
2

Im Jahr 2019 würde ich vorschlagen, alle Elementanimationen in den ItemAnimator einzufügen.

Beginnen wir mit der Deklaration des Animators in der Recycler-Ansicht:

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

Deklarieren Sie dann den benutzerdefinierten Animator.

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

}

Ähnlich wie oben gibt es eine für das Verschwinden animateDisappearance, für das Hinzufügen animateAdd, für das Ändern animateChangeund Bewegen animateMove.

Ein wichtiger Punkt wäre, die richtigen Animations-Dispatcher in ihnen anzurufen.

Dinesh
quelle
Können Sie mit dieser Überschreibungsfunktion ein Beispiel für eine benutzerdefinierte Darstellungsanimation bereitstellen? Ich kann kein Beispiel finden und bin mir nicht sicher, ob ich nur die Animation in der Funktion angeben muss.
Minar
1
gist.github.com/tadfisher/120d03f8380bfa8a16bf Ich habe dies online bei einer Schnellsuche gefunden. Dies gibt eine Vorstellung davon, wie die Überladung funktioniert
Dinesh,
0

Erweitert einfach Ihren Adapter wie unten

public class RankingAdapter extends AnimatedRecyclerView<RankingAdapter.ViewHolder> 

Und fügen Sie onBindViewHolder eine Super-Methode hinzu

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

Es ist eine automatisierte Möglichkeit, animierte Adapter wie "Basheer AL-MOMANI" zu erstellen.

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

}
EliaszKubala
quelle
0

Ich denke, es ist besser, es so zu verwenden: (im RecyclerView- Adapter überschreiben Sie nur eine einzige Methode)

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

    setBindAnimation(holder)
}

Wenn Sie möchten, dass jede Animation dort in RV angehängt wird.

Milan Jurkulak
quelle