So animieren Sie das Hinzufügen oder Entfernen von Android ListView-Zeilen


212

In iOS gibt es eine sehr einfache und leistungsstarke Funktion zum Animieren des Hinzufügens und Entfernens von UITableView-Zeilen. Hier ist ein Clip aus einem Youtube-Video, das die Standardanimation zeigt. Beachten Sie, wie die umgebenden Zeilen auf die gelöschte Zeile fallen. Diese Animation hilft Benutzern zu verfolgen, was sich in einer Liste geändert hat und wo in der Liste sie sich angesehen haben, als sich die Daten geändert haben.

Seit ich auf Android entwickelt habe, habe ich keine gleichwertige Möglichkeit gefunden, einzelne Zeilen in einer TableView zu animieren . Der Aufruf notifyDataSetChanged()auf meinem Adapter bewirkt , dass der Listview sofort seinen Inhalt mit neuen Informationen zu aktualisieren. Ich möchte eine einfache Animation einer neuen Zeile zeigen, die sich ein- oder ausschiebt, wenn sich die Daten ändern, aber ich kann keinen dokumentierten Weg finden, dies zu tun. Es sieht so aus, als ob LayoutAnimationController einen Schlüssel enthält, um dies zum Laufen zu bringen, aber wenn ich einen LayoutAnimationController in meiner ListView einstelle (ähnlich wie LayoutAnimation2 von ApiDemo ) einstelle und Elemente aus meinem Adapter entferne, nachdem die Liste angezeigt wurde, verschwinden die Elemente sofort, anstatt animiert zu werden .

Ich habe auch Folgendes versucht, um ein einzelnes Element zu animieren, wenn es entfernt wird:

@Override
protected void onListItemClick(ListView l, View v, final int position, long id) {
    Animation animation = new ScaleAnimation(1, 1, 1, 0);
    animation.setDuration(100);
    getListView().getChildAt(position).startAnimation(animation);
    l.postDelayed(new Runnable() {
        public void run() {
            mStringList.remove(position);
            mAdapter.notifyDataSetChanged();
        }
    }, 100);
}

Die die animierte Zeile umgebenden Zeilen verschieben jedoch ihre Position erst, wenn sie zu ihren neuen Positionen springen notifyDataSetChanged() Aufruf . Es scheint, dass ListView sein Layout nicht aktualisiert, sobald seine Elemente platziert wurden.

Während mir das Schreiben meiner eigenen Implementierung / Fork von ListView in den Sinn gekommen ist, scheint dies etwas zu sein, das nicht so schwierig sein sollte.

Vielen Dank!


hat jemand die Antwort darauf gefunden? pls shareee
AndroidGecko

@Alex: Hast du diese Animation wie dieses Youtube-Video gemacht (wie iPhone)? Wenn du eine Demo oder einen Link für Android hast, dann gib mir bitte, ich habe versucht, animateLayoutChanges in einer XML-Datei zu verwenden, aber es ist nicht genau wie iPhone
Jayesh

@ Jayesh, leider arbeite ich nicht mehr an der Android-Entwicklung. Ich habe keine der Antworten auf diese Frage getestet, die nach ungefähr 2012 geschrieben wurden.
Alex Pretzlav

Antworten:


124
Animation anim = AnimationUtils.loadAnimation(
                     GoTransitApp.this, android.R.anim.slide_out_right
                 );
anim.setDuration(500);
listView.getChildAt(index).startAnimation(anim );

new Handler().postDelayed(new Runnable() {

    public void run() {

        FavouritesManager.getInstance().remove(
            FavouritesManager.getInstance().getTripManagerAtIndex(index)
        );
        populateList();
        adapter.notifyDataSetChanged();

    }

}, anim.getDuration());

für Animationen von oben nach unten:

<set xmlns:android="http://schemas.android.com/apk/res/android">
        <translate android:fromYDelta="20%p" android:toYDelta="-20"
            android:duration="@android:integer/config_mediumAnimTime"/>
        <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
            android:duration="@android:integer/config_mediumAnimTime" />
</set>

70
Es ist besser zu verwenden anim.setAnimationListener(new AnimationListener() { ... }), als einen Handler zu verwenden
Oleg Vaskevich

1
was populateList()für?
Vasily Sochinsky

5
@ MrAppleBR aus mehreren Gründen. Erstens erstellen Sie keine unnötigen Handler. Zweitens wird der Code weniger komplex, da bereits ein integrierter Rückruf verwendet wird, wenn die Animation beendet ist. Drittens wissen Sie nicht, wie die Implementierung auf jeder Plattform funktioniert Handlerund Animationwie sie funktionieren wird. Möglicherweise tritt Ihre verzögerte Ausführung vor Abschluss der Animation auf.
Oleg Vaskevich

7
"Die Zeilen, die die animierte Zeile umgeben, verschieben ihre Position jedoch erst, wenn sie beim Aufruf von notifyDataSetChanged () an ihre neuen Positionen springen." Dieses Problem besteht weiterhin in Ihrer Lösung.
Boris Rusev

5
Was ist die FavouritesManager-Klasse und die populateList () -Methode?
Jayesh

14

1
Ich denke, dies ist der einfache Weg, wenn Sie eine einfache Animation benötigen, aber Sie können die Animation auch mit der RecyclerView.setItemAnimator () -Methode anpassen.
Qatz

2
Der letzte Schritt ist das fehlende Glied, das ich hatte. stabile IDs. Vielen Dank!
Amir Uval

Dies ist ein großartiges Beispielprojekt, das die API gut demonstriert.
Mr-IDE

9

Schauen Sie sich die Google-Lösung an. Hier ist nur eine Löschmethode.

ListViewRemovalAnimation- Projektcode und Video - Demonstration

Es benötigt Android 4.1+ (API 16). Aber wir haben 2014 draußen.


2

Da sie ListViewsstark optimiert sind, denke ich, dass dies nicht möglich ist, dies zu erreichen. Haben Sie versucht, Ihre "ListView" per Code zu erstellen (dh indem Sie Ihre Zeilen aus XML aufblasen und an a anhängen LinearLayout) und sie animieren?


6
Es ist nicht sinnvoll, die Listenansicht neu zu implementieren, nur um Animationen hinzuzufügen.
Macarse

Ich könnte sicherlich nur eine verwenden LinearLayout, aber bei sehr großen Datenmengen wird LinearLayoutdie Leistung miserabel. ListViewEs gibt viele intelligente Optimierungen für das Recycling von Ansichten, die es ermöglichen, große Datenmengen zu verarbeiten (UITableView von iOS verfügt über dieselben Optimierungen). Das Schreiben meiner eigenen Ansicht Recycler fällt unter die Kategorie der Neuimplementierung von ListView :(
Alex Pretzlav

Ich weiß, dass es keinen Sinn macht - aber wenn Sie mit nur wenigen Zeilen arbeiten, könnte der Vorteil netter In / Out-Animationen den Versuch wert sein.
whlk

2

Haben Sie darüber nachgedacht, einen Sweep nach rechts zu animieren? Sie können beispielsweise einen zunehmend größeren weißen Balken über das Listenelement ziehen und ihn dann aus der Liste entfernen. Die anderen Zellen würden immer noch einrasten, aber es wäre besser als nichts.


2

call listView.scheduleLayoutAnimation (); bevor Sie die Liste ändern


2

Ich habe einen anderen Weg zusammen gehackt, ohne die Listenansicht manipulieren zu müssen. Leider scheinen reguläre Android-Animationen den Inhalt der Zeile zu manipulieren, sind jedoch nicht effektiv, um die Ansicht tatsächlich zu verkleinern. Betrachten Sie also zuerst diesen Handler:

private Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
    Bundle bundle = message.getData();

    View view = listView.getChildAt(bundle.getInt("viewPosition") - 
        listView.getFirstVisiblePosition());

    int heightToSet;
    if(!bundle.containsKey("viewHeight")) {
        Rect rect = new Rect();
        view.getDrawingRect(rect);
        heightToSet = rect.height() - 1;
    } else {
        heightToSet = bundle.getInt("viewHeight");
    }

    setViewHeight(view, heightToSet);

    if(heightToSet == 1)
        return;

    Message nextMessage = obtainMessage();
    bundle.putInt("viewHeight", (heightToSet - 5 > 0) ? heightToSet - 5 : 1);
    nextMessage.setData(bundle);
    sendMessage(nextMessage);
}

Fügen Sie diese Sammlung Ihrem Listenadapter hinzu:

private Collection<Integer> disabledViews = new ArrayList<Integer>();

und hinzufügen

public boolean isEnabled(int position) {
   return !disabledViews.contains(position);
}

Fügen Sie als Nächstes, wo immer Sie eine Zeile ausblenden möchten, Folgendes hinzu:

Message message = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putInt("viewPosition", listView.getPositionForView(view));
message.setData(bundle);
handler.sendMessage(message);    
disabledViews.add(listView.getPositionForView(view));

Das ist es! Sie können die Geschwindigkeit der Animation ändern, indem Sie die Anzahl der Pixel ändern, um die die Höhe gleichzeitig verringert wird. Nicht wirklich raffiniert, aber es funktioniert!


2

Nachdem ich eine neue Zeile in ListView eingefügt habe, scrolle ich einfach die ListView an eine neue Position.

ListView.smoothScrollToPosition(position);

1

Ich habe es nicht ausprobiert, aber es sieht so aus, als ob animateLayoutChanges das tun sollte, wonach Sie suchen. Ich sehe es in der ImageSwitcher-Klasse. Ich nehme an, es ist auch in der ViewSwitcher-Klasse.


Hey danke, ich werde das untersuchen. Leider sieht es so aus, als ob animateLayoutChanges ab API Level 11, auch bekannt als Honeycomb, neu ist. Dies kann noch auf keinem Telefon verwendet werden:
Alex Pretzlav

1

Da Android Open Source ist, müssen Sie die Optimierungen von ListView nicht erneut implementieren. Sie können den Code von ListView abrufen und versuchen, einen Weg zu finden, die Animation zu hacken. Sie können auch eine Funktionsanforderung im Android Bug Tracker öffnen (und wenn Sie sich für die Implementierung entschieden haben, vergessen Sie nicht, einen Patch beizusteuern ).

Zu Ihrer Information, der ListView-Quellcode ist hier .


1
Auf diese Weise sollten solche Änderungen nicht implementiert werden. Vor allem sollten Sie das AOSP-Projekt erstellen - ein ziemlich umständlicher Ansatz zum Hinzufügen von Animationen zu Listenansichten. Bitte schauen Sie sich Implementierungen in verschiedenen Open Source-Bibliotheken an.
OrhanC1

1

Hier ist der Quellcode , mit dem Sie Zeilen löschen und neu anordnen können.

Eine Demo-APK-Datei ist ebenfalls verfügbar. Das Löschen von Zeilen erfolgt eher in Anlehnung an die Google Mail-App, die nach dem Wischen einer Draufsicht eine Unteransicht anzeigt. Die Unteransicht kann eine Schaltfläche zum Rückgängigmachen oder was auch immer Sie wollen haben.


1

Wie ich meinen Ansatz auf meiner Website erklärt hatte, habe ich den Link geteilt. Die Idee ist immer, Bitmaps mit getdrawingcache zu erstellen. Habe zwei Bitmaps und animiere die untere Bitmap, um den Bewegungseffekt zu erzeugen

Bitte beachten Sie den folgenden Code:

listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
    {
        public void onItemClick(AdapterView<?> parent, View rowView, int positon, long id)
        {
            listView.setDrawingCacheEnabled(true);
            //listView.buildDrawingCache(true);
            bitmap = listView.getDrawingCache();
            myBitmap1 = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), rowView.getBottom());
            myBitmap2 = Bitmap.createBitmap(bitmap, 0, rowView.getBottom(), bitmap.getWidth(), bitmap.getHeight() - myBitmap1.getHeight());
            listView.setDrawingCacheEnabled(false);
            imgView1.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap1));
            imgView2.setBackgroundDrawable(new BitmapDrawable(getResources(), myBitmap2));
            imgView1.setVisibility(View.VISIBLE);
            imgView2.setVisibility(View.VISIBLE);
            RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
            lp.setMargins(0, rowView.getBottom(), 0, 0);
            imgView2.setLayoutParams(lp);
            TranslateAnimation transanim = new TranslateAnimation(0, 0, 0, -rowView.getHeight());
            transanim.setDuration(400);
            transanim.setAnimationListener(new Animation.AnimationListener()
            {
                public void onAnimationStart(Animation animation)
                {
                }

                public void onAnimationRepeat(Animation animation)
                {
                }

                public void onAnimationEnd(Animation animation)
                {
                    imgView1.setVisibility(View.GONE);
                    imgView2.setVisibility(View.GONE);
                }
            });
            array.remove(positon);
            adapter.notifyDataSetChanged();
            imgView2.startAnimation(transanim);
        }
    });

Zum Verständnis mit Bildern siehe dies

Vielen Dank.


0

Ich habe etwas Ähnliches getan. Ein Ansatz besteht darin, über die Animationszeit die Höhe der Ansicht über die Zeit innerhalb der Zeilen zu interpolieren, onMeasurewährend requestLayout()für die listView ausgegeben wird. Ja, es ist vielleicht besser, direkt im listView-Code zu arbeiten, aber es war eine schnelle Lösung (das sah gut aus!)


0

Teilen Sie einfach einen anderen Ansatz:

Setzen Sie zuerst das Android: animateLayoutChanges der Listenansicht auf true :

<ListView
        android:id="@+id/items_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"/>

Dann benutze ich einen Handler , um Elemente hinzuzufügen und die Listenansicht mit Verzögerung zu aktualisieren:

Handler mHandler = new Handler();
    //delay in milliseconds
    private int mInitialDelay = 1000;
    private final int DELAY_OFFSET = 1000;


public void addItem(final Integer item) {
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mDataSet.add(item);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mAdapter.notifyDataSetChanged();
                        }
                    });
                }
            }).start();

        }
    }, mInitialDelay);
    mInitialDelay += DELAY_OFFSET;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.