web-dev-qa-db-fra.com

Comment animer l'ajout ou la suppression de rangées Android ListView

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

Depuis que je développe sur Android, je n'ai trouvé aucune possibilité équivalente d'animer des lignes individuelles dans un TableView . Lorsque vous appelez notifyDataSetChanged() on my Adapter, ListView met immédiatement à jour son contenu avec les nouvelles informations. J'aimerais montrer une animation simple d'une nouvelle ligne qui entre ou qui disparaît lorsque les données changent, mais je ne trouve aucun moyen documenté de le faire. Il semble que LayoutAnimationController pourrait contenir une clé pour que cela fonctionne, mais lorsque je définis un LayoutAnimationController sur mon ListView (similaire à ApiDemo's LayoutAnimation2 ) et que je supprime des éléments de mon adaptateur une fois la liste affichée, les éléments disparaissent immédiatement au lieu de s'animer.

J'ai également essayé, entre autres, d'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);
}

Toutefois, les lignes entourant la ligne animée ne bougent pas de position tant qu'elles ne sautent pas à leur nouvelle position lorsque notifyDataSetChanged() est appelé. Apparemment, ListView ne met pas à jour sa mise en page une fois ses éléments placés.

Bien que l’écriture de ma propre implémentation/grille de ListView m’ait traversé l’esprit, cela semble être quelque chose qui ne devrait pas être aussi difficile.

Merci!

206
Alex Pretzlav
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>
122
OAK
14
Eric

Jetez un coup d'oeil à la solution de Google. Voici une méthode de suppression seulement.

ListViewRemovalAnimation code du projet et Vidéo démonstration

Il nécessite Android 4.1+ (API 16). Mais nous avons 2014 à l'extérieur.

9
dimetil

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

2
karabara

Avez-vous envisagé d'animer un balayage vers la droite? Vous pouvez par exemple dessiner une barre blanche progressivement plus grande en haut de l’élément de la liste, puis la supprimer de la liste. Les autres cellules resteraient encore saccadées, mais ce serait mieux que rien.

2
Sam Dufel

Après avoir inséré une nouvelle ligne dans ListView, je viens de faire défiler le ListView vers une nouvelle position. 

ListView.smoothScrollToPosition(position);
2
Rafael

J'ai piraté une autre façon de le faire sans avoir à manipuler la vue 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érons 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 qu'il réduit en même temps. Pas vraiment sophistiqué, mais ça marche!

2
jkschneider

Puisque ListViews sont hautement optimisés, je pense que cela n’est pas possible. Avez-vous essayé de créer votre "ListView" par code (c'est-à-dire en gonflant vos lignes à partir de xml et en les ajoutant à une LinearLayout) et de les animer?

2
whlk

Étant donné qu'Android est open source, vous n'avez pas 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 aussi ouvrir une demande de fonctionnalité dans le suivi des bugs Android (et si vous avez décidé de l'implémenter, n'oubliez pas de contribuer à patch ). 

Pour votre information, le code source de ListView est here .

1
Lie Ryan

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

1
Genia S.

Comme je l'avais expliqué dans mon site, j'ai partagé le lien. De toute façon, l'idée est de créer des bitmaps À l'aide de getdrawingcache. Avoir deux bitmap et d'animer le bitmap inférieur pour créer l'effet de déplacement

S'il vous plaît voir 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 en images voir ceci

Merci.

1
Ram

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

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

1
AndroidDev

J'ai fait quelque chose de semblable à ceci. Une approche consiste à interpoler sur le temps d'animation la hauteur de la vue au fil du temps à l'intérieur des lignes onMeasure lors de l'émission de requestLayout() pour listView. Oui, il serait peut-être préférable de le faire directement à l'intérieur du code listView, mais c'était une solution rapide (ça avait l'air bien!) 

0
Dori

Juste partager une autre approche:

Commencez par définir la vue en liste Android: animateLayoutChanges to true :

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

Ensuite, j'utilise un handler pour ajouter des éléments et mettre à jour la listview avec délai:

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