Comment animer l'ajout ou la suppression de lignes ListView Android

212

Dans iOS, il existe une fonction très simple et puissante pour animer l'ajout et la suppression de lignes UITableView, voici un extrait d'une vidéo youtube montrant l'animation par défaut. Notez comment les lignes environnantes se replient sur la ligne supprimée. Cette animation aide les utilisateurs à suivre ce qui a changé dans une liste et où dans la liste ils regardaient quand les données ont changé.

Depuis que je développe sur Android, je n'ai trouvé aucune facilité équivalente pour animer des lignes individuelles dans une TableView . En appelant notifyDataSetChanged()mon adaptateur, ListView met immédiatement à jour son contenu avec de nouvelles informations. Je voudrais montrer une animation simple d'une nouvelle ligne poussant ou glissant lorsque les données changent, mais je ne trouve aucun moyen documenté de le faire. Il semble que LayoutAnimationController puisse détenir une clé pour que cela fonctionne, mais lorsque je définis un LayoutAnimationController sur ma ListView (similaire à LayoutAnimation2 d'ApiDemo ) et que je supprime des éléments de mon adaptateur après l'affichage de la liste, les éléments disparaissent immédiatement au lieu d'être animés. .

J'ai également essayé des choses comme les suivantes pour animer un élément individuel lorsqu'il est supprimé:

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

Cependant, les lignes entourant la ligne animée ne bougent pas jusqu'à ce qu'elles sautent à leur nouvelle position lors de l' notifyDataSetChanged()appel. Il semble que ListView ne met pas à jour sa disposition une fois ses éléments placés.

Bien que l'écriture de ma propre implémentation / fork de ListView me soit venue à l'esprit, cela semble être quelque chose qui ne devrait pas être si difficile.

Merci!

Alex Pretzlav
la source
quelqu'un a-t-il trouvé la réponse à cela? pls shareee
AndroidGecko
@Alex: avez-vous fait cette animation comme cette vidéo youtube (comme l'iphone), si vous avez une démo ou un lien pour android alors donnez-moi s'il vous plaît, j'ai essayé d'utiliser animateLayoutChanges dans le fichier xml, mais ce n'est pas exactement comme l'iphone
Jayesh
@Jayesh, malheureusement je ne travaille plus sur le développement Android. Je n'ai testé aucune des réponses à cette question qui ont été écrites après environ 2012.
Alex Pretzlav

Réponses:

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

pour une animation de haut en bas:

<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>
CHÊNE
la source
70
Il vaut mieux utiliser anim.setAnimationListener(new AnimationListener() { ... })au lieu d'utiliser un gestionnaire
Oleg Vaskevich
1
à quoi ça sert populateList()?
Vasily Sochinsky
5
@MrAppleBR pour plusieurs raisons. Tout d'abord, vous ne créez pas d'inutile Handler. Deuxièmement, il rend le code moins complexe en utilisant déjà un rappel intégré pour la fin de l'animation. Troisièmement, vous ne savez pas comment la mise en œuvre de Handleret Animationfonctionnera sur chaque plate-forme; il est possible que votre exécutable retardé se produise avant la fin de l'animation.
Oleg Vaskevich
7
"Cependant, les lignes entourant la ligne animée ne se déplacent pas jusqu'à ce qu'elles sautent à leurs nouvelles positions lors de l'appel de notifyDataSetChanged ()." ce problème persiste dans votre solution.
Boris Rusev
5
Qu'est-ce que la classe FavouritesManager et la méthode populateList () ??
Jayesh
14

Le RecyclerViewprend en charge l'ajout, la suppression et la réorganisation des animations!

RecyclerView en action

Ce projet AndroidStudio simple comprend RecyclerView: jetez un œil aux commits :

  1. validation de l'application Android Hello World classique
  2. commit, ajout d'une RecyclerView au projet (contenu non dynamique)
  3. validation, ajout de fonctionnalités pour modifier le contenu de RecyclerView lors de l'exécution (mais pas d'animations)
  4. et enfin ... valider l'ajout d'animations à RecyclerView
Eric
la source
1
Je pense que c'est la manière simple lorsque vous avez besoin d'une animation simple, mais vous pouvez également personnaliser l'animation en utilisant la méthode RecyclerView.setItemAnimator ().
qatz
2
La dernière étape est le chaînon manquant que j'avais. Id stable. Merci!
Amir Uval
Il s'agit d'un excellent exemple de projet qui illustre bien l'API.
Mr-IDE
9

Jetez un œil à la solution Google. Voici une méthode de suppression uniquement.

Code de projet ListViewRemovalAnimation et démonstration vidéo

Il a besoin d'Android 4.1+ (API 16). Mais nous avons 2014 à l'extérieur.

dimétil
la source
2

Étant donné qu'ils ListViewssont hautement optimisés, je pense que ce n'est pas possible de répondre. Avez-vous essayé de créer votre "ListView" par code (c'est-à-dire en gonflant vos lignes de xml et en les ajoutant à a LinearLayout) et en les animant?

whlk
la source
6
Il n'est pas logique de réimplémenter la vue de liste simplement pour ajouter des animations.
Macarse
Je pourrais certainement utiliser un LinearLayout, mais avec de très grands ensembles LinearLayoutde données , les performances deviendront abyssales. ListViewa beaucoup d'optimisations intelligentes autour du recyclage des vues qui lui permettent de gérer de grands ensembles de données (UITableView d'iOS a les mêmes optimisations). Écrire mon propre recycleur de vues tombe dans la catégorie de la réimplémentation de ListView :(
Alex Pretzlav
Je sais que cela n'a pas de sens - mais si vous travaillez avec seulement quelques lignes, l'avantage d'avoir de belles animations d'entrée / sortie pourrait valoir la peine d'être essayé.
whlk
2

Avez-vous pensé à animer un balayage vers la droite? Vous pouvez faire quelque chose comme dessiner une barre blanche progressivement plus grande en haut de l'élément de liste, puis la supprimer de la liste. Les autres cellules continueraient à se mettre en place, mais ce serait mieux que rien.

Sam Dufel
la source
2

appelez listView.scheduleLayoutAnimation (); avant de changer la liste

Karabara
la source
2

J'ai piraté ensemble une autre façon de le faire sans avoir à manipuler la vue de liste. Malheureusement, les animations Android régulières semblent manipuler le contenu de la ligne, mais ne permettent pas de réduire la vue. Alors, considérez d'abord ce gestionnaire:

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

Ajoutez cette collection à votre adaptateur de liste:

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

et ajouter

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

Ensuite, où que vous souhaitiez masquer une ligne, ajoutez ceci:

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

C'est tout! Vous pouvez modifier la vitesse de l'animation en modifiant le nombre de pixels dont elle réduit la hauteur à la fois. Pas vraiment sophistiqué, mais ça marche!

jkschneider
la source
2

Après avoir inséré une nouvelle ligne dans ListView, je fais simplement défiler ListView vers une nouvelle position.

ListView.smoothScrollToPosition(position);
Rafael
la source
1

Je ne l'ai pas essayé, mais il semble que animateLayoutChanges devrait faire ce que vous recherchez. Je le vois dans la classe ImageSwitcher, je suppose que c'est aussi dans la classe ViewSwitcher?

Yevgeny Simkin
la source
Hé merci, je vais voir ça. Malheureusement, il semble que animateLayoutChanges soit nouveau à partir de l'API niveau 11 aka Honeycomb aka Can't - Use -this-on-any-phones-yet :(
Alex Pretzlav
1

Comme Android est open source, vous n'avez pas réellement besoin de réimplémenter les optimisations de ListView. Vous pouvez récupérer le code de ListView et essayer de trouver un moyen de pirater l'animation, vous pouvez également ouvrir une demande de fonctionnalité dans le suivi des bogues Android (et si vous avez décidé de l'implémenter, n'oubliez pas de contribuer un correctif ).

Pour info, le code source de ListView est ici .

Lie Ryan
la source
1
Ce n'est pas ainsi que ces changements doivent être mis en œuvre. Avant toute chose, vous devriez avoir le projet de construction AOSP - qui est une approche assez lourde pour ajouter une animation aux vues de liste. Veuillez regarder les implémentations dans diverses bibliothèques open source.
OrhanC1
1

Voici le code source pour vous permettre de supprimer des lignes et de les réorganiser.

Un fichier APK de démonstration est également disponible. La suppression des lignes se fait plus dans le sens de l'application Gmail de Google qui révèle une vue de dessous après avoir glissé une vue de dessus. La vue inférieure peut avoir un bouton Annuler ou tout ce que vous voulez.

AndroidDev
la source
1

Comme je l'avais expliqué dans mon site, j'ai partagé le lien. Quoi qu'il en soit, l'idée est de créer des bitmaps par getdrawingcache.

Veuillez consulter le code suivant:

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

Pour comprendre les images, voir ceci

Merci.

RAM
la source
0

J'ai fait quelque chose de similaire à cela. Une approche consiste à interpoler sur la durée de l'animation la hauteur de la vue dans le temps à l'intérieur des lignes onMeasurelors de l'émission requestLayout()pour listView. Oui, il peut être préférable de le faire directement dans le code listView mais c'était une solution rapide (qui avait l'air bien!)

Dori
la source
0

Partageons simplement une autre approche:

Définissez d' abord l' androïde de la vue de liste : animateLayoutChanges sur true :

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

Ensuite, j'utilise un gestionnaire pour ajouter des éléments et mettre à jour la vue de liste avec retard:

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;
}
Mina Samy
la source