Come animare l'aggiunta o la rimozione delle righe di ListView per Android


212

In iOS, esiste una funzione molto semplice e potente per animare l'aggiunta e la rimozione di righe UITableView, ecco una clip da un video di YouTube che mostra l'animazione predefinita. Nota come le righe circostanti collassano sulla riga eliminata. Questa animazione aiuta gli utenti a tenere traccia di ciò che è cambiato in un elenco e dove nell'elenco stavano guardando quando i dati sono cambiati.

Da quando ho sviluppato su Android non ho trovato strutture equivalenti per animare singole righe in un TableView . La chiamata notifyDataSetChanged()al mio adattatore fa sì che ListView aggiorni immediatamente il suo contenuto con nuove informazioni. Vorrei mostrare una semplice animazione di una nuova riga che spinge verso l'interno o scivola quando i dati cambiano, ma non riesco a trovare alcun modo documentato per farlo. Sembra che LayoutAnimationController potrebbe contenere una chiave per farlo funzionare, ma quando imposto un LayoutAnimationController sul mio ListView (simile a LayoutAnimation2 di ApiDemo ) e rimuovo gli elementi dal mio adattatore dopo che l'elenco è stato visualizzato, gli elementi scompaiono immediatamente invece di animarsi .

Ho anche provato cose come le seguenti per animare un singolo elemento quando viene rimosso:

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

Tuttavia, le righe che circondano la riga animata non si spostano fino a quando non saltano alle loro nuove posizioni quando notifyDataSetChanged()viene chiamato. Sembra che ListView non aggiorni il suo layout una volta posizionati i suoi elementi.

Mentre scrivevo la mia implementazione / fork di ListView mi è passato per la testa, questo sembra qualcosa che non dovrebbe essere così difficile.

Grazie!


qualcuno ha trovato la risposta a questo? pls shareee
AndroidGecko,

@Alex: hai fatto quell'animazione come quel video di YouTube (come iPhone), se hai qualche demo o collegamento per Android, per favore, dammi, ho provato a usare animateLayoutChanges in file xml, ma non è esattamente come iPhone
Jayesh

@Jayesh, sfortunatamente non lavoro più sullo sviluppo Android. Non ho testato nessuna delle risposte a questa domanda scritte dopo il 2012.
Alex Pretzlav,

Risposte:


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

per l'uso dell'animazione dall'alto verso il basso:

<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
È meglio usare anim.setAnimationListener(new AnimationListener() { ... })invece di usare un gestore
Oleg Vaskevich il

1
a cosa serve populateList()?
Vasily Sochinsky,

5
@MrAppleBR per diversi motivi. Innanzitutto, non stai creando un non necessario Handler. In secondo luogo, rende il codice meno complesso utilizzando già un callback incorporato per il completamento dell'animazione. Terzo, non sai come implementare Handlere Animationfunzionerà su ciascuna piattaforma; potrebbe essere possibile che la tua esecuzione ritardata si verifichi prima che l'animazione finisca.
Oleg Vaskevich,

7
"Tuttavia, le righe che circondano la riga animata non si spostano fino a quando non saltano alle loro nuove posizioni quando viene chiamato notificationDataSetChanged ()." questo problema persiste ancora nella tua soluzione.
Boris Rusev,

5
Cos'è la classe FavouritesManager e il metodo populateList () ??
Jayesh,

14

1
Penso che questo sia il modo semplice quando hai bisogno di una semplice animazione, ma puoi anche personalizzare l'animazione usando il metodo RecyclerView.setItemAnimator ().
qatz

2
Il passaggio finale è il collegamento mancante che avevo. ID stabile. Grazie!
Amir Uval,

Questo è un ottimo progetto di esempio che dimostra bene l'API.
Mr-IDE,

9

Dai un'occhiata alla soluzione di Google. Ecco solo un metodo di eliminazione.

ListViewRemovalAnimation codice progetto e dimostrazione video

È necessario Android 4.1+ (API 16). Ma abbiamo 2014 fuori.


2

Dato che ListViewssono altamente ottimizzati, penso che non sia possibile accedervi. Hai provato a creare il tuo "ListView" per codice (cioè gonfiando le tue righe da XML e aggiungendole a LinearLayout) e animandole?


6
Non ha senso reimplementare la vista elenco solo per aggiungere animazioni.
Macarse,

Certamente potrei semplicemente usare un LinearLayout, tuttavia con LinearLayoutprestazioni di set di dati molto grandi diventerà spaventoso. ListViewha molte ottimizzazioni intelligenti per il riciclo della vista che gli consentono di gestire grandi set di dati (UITableView di iOS ha le stesse ottimizzazioni). Scrivere il mio punto di vista riciclatore rientra nella categoria di reimplementazione di ListView :(
Alex Pretzlav,

So che non ha senso - ma se lavori con solo poche righe, vale la pena provare il vantaggio di avere delle belle animazioni in / out.
whlk

2

Hai mai pensato di animare uno sweep a destra? Potresti fare qualcosa come disegnare una barra bianca progressivamente più grande nella parte superiore dell'elemento dell'elenco, quindi rimuoverlo dall'elenco. Le altre celle sarebbero ancora scattate al loro posto, ma sarebbe meglio di niente.


2

call listView.scheduleLayoutAnimation (); prima di cambiare l'elenco


2

Ho combinato un altro modo per farlo senza dover manipolare la visualizzazione elenco. Sfortunatamente, le normali animazioni Android sembrano manipolare i contenuti della riga, ma sono inefficaci nel ridurre effettivamente la vista. Quindi, prima considera questo gestore:

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

Aggiungi questa raccolta all'adattatore Elenco:

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

e aggiungi

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

Quindi, ovunque sia che desideri nascondere una riga, aggiungi questo:

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

Questo è tutto! È possibile modificare la velocità dell'animazione modificando il numero di pixel che riduce l'altezza in una sola volta. Non molto sofisticato, ma funziona!


2

Dopo aver inserito una nuova riga in ListView, faccio scorrere il ListView in una nuova posizione.

ListView.smoothScrollToPosition(position);

1

Non l'ho provato ma sembra che animateLayoutChanges dovrebbe fare quello che stai cercando. Lo vedo nella classe ImageSwitcher, presumo sia anche nella classe ViewSwitcher?


Ehi grazie, lo esaminerò. Sfortunatamente sembra che animateLayoutChanges sia nuovo a partire da API Level 11 aka Honeycomb aka non può ancora usare questo telefono su nessun telefono :(
Alex Pretzlav il

1

Poiché Android è open source, in realtà non è necessario reimplementare le ottimizzazioni di ListView. Puoi prendere il codice di ListView e provare a trovare un modo per hackerare l'animazione, puoi anche aprire una richiesta di funzionalità nel tracker dei bug di Android (e se hai deciso di implementarlo, non dimenticare di contribuire con una patch ).

Cordiali saluti, il codice sorgente di ListView è qui .


1
Questo non è il modo in cui tali cambiamenti dovrebbero essere implementati. Prima di tutto, dovresti avere la costruzione del progetto AOSP, che è un approccio piuttosto pesante per aggiungere animazione alle visualizzazioni elenco. Si prega di guardare le implementazioni in varie librerie open source.
OrhanC1

1

Ecco il codice sorgente per consentire di eliminare le righe e riordinarle.

È anche disponibile un file APK demo. L'eliminazione delle righe viene eseguita più lungo le linee dell'app Gmail di Google che rivela una vista dal basso dopo aver fatto scorrere una vista dall'alto. La vista dal basso può avere un pulsante Annulla o quello che vuoi.


1

Come ho spiegato il mio approccio nel mio sito, ho condiviso il link. Comunque l'idea è di creare bitmap ottenendo cache. Hanno due bitmap e animano la bitmap inferiore per creare l'effetto in movimento

Si prega di consultare il seguente codice:

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

Per capire con le immagini vedi questo

Grazie.


0

Ho fatto qualcosa di simile a questo. Un approccio consiste nell'interpolare nel tempo dell'animazione l'altezza della vista nel tempo all'interno delle righe onMeasuredurante l'emissione requestLayout()per listView. Sì, potrebbe essere meglio fare direttamente all'interno del codice listView ma è stata una soluzione rapida (che sembrava buona!)


0

Basta condividere un altro approccio:

Innanzitutto imposta Android della vista elenco : animateLayoutChanges su true :

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

Quindi utilizzo un gestore per aggiungere elementi e aggiornare la visualizzazione elenco con ritardo:

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