web-dev-qa-db-fra.com

Android ExpandableListView utilisant une animation

J'utilise

<ExpandableListView
     Android:id="@+id/listView"
     Android:layout_width="match_parent"
     Android:layout_height="match_parent" >
</ExpandableListView>

je veux ajouter une diapositive d'animation pour l'enfant lorsque l'onclique parent. Alors, comment faire?

15
Lê Minh Quyền

Mise à jour finale

Cela fait un bon moment que j'ai écrit cette réponse. Depuis lors, beaucoup de choses ont changé. Le plus grand changement est avec l'introduction de RecyclerView qui facilite l'animation d'une liste ou d'une grille. Je vous recommande fortement de passer à RecyclerViews si vous le pouvez. Pour ceux qui ne le peuvent pas, je vais voir ce que je peux faire pour corriger les bugs de ma bibliothèque.


Réponse originale

En fait, je n'aime pas l'implémentation populaire d'une ExpandableListView animée qui utilise simplement une ListView avec une animation développée car dans mon cas d'utilisation, chacun de mes groupes avait beaucoup d'enfants, donc il n'était pas possible d'utiliser une ListView normale comme enfant les vues ne seront pas recyclées et l'utilisation de la mémoire sera énorme avec de mauvaises performances. Au lieu de cela, j'ai opté pour une approche beaucoup plus difficile mais plus évolutive et flexible.

J'ai étendu la classe ExpandableListView et remplacé les fonctions onCollapse et onExpand, j'ai également créé une sous-classe d'un BaseExpandableListAdapter appelé AnimatedExpandableListAdapter. À l'intérieur de l'adaptateur, j'ai remplacé la fonction getChildView et rendu la fonction finale afin que la fonction ne puisse pas être à nouveau remplacée. Au lieu de cela, j'ai fourni une autre fonction appelée getRealChildView pour les sous-classes à remplacer pour fournir une véritable vue enfant. J'ai ensuite ajouté un indicateur d'animation à la classe et demandé à getChildView de renvoyer une vue factice si l'indicateur d'animation était défini et la vue réelle si l'indicateur n'était pas défini. Maintenant, avec le décor, je fais ce qui suit pour onExpand:

  1. Définissez l'indicateur d'animation dans l'adaptateur et indiquez à l'adaptateur quel groupe se développe.
  2. Appelez notifyDataSetChanged() (force l'adaptateur à appeler getChildView() pour toutes les vues à l'écran).
  3. L'adaptateur (en mode animation) créera ensuite une vue factice pour le groupe en expansion qui a la hauteur initiale 0. L'adaptateur récupérera alors les vues enfants réelles et transmettra ces vues à la vue factice.
  4. La vue factice commencera alors à dessiner les vraies vues enfant dans sa propre fonction onDraw().
  5. L'adaptateur lancera une boucle d'animation qui élargira la vue factice jusqu'à ce qu'elle soit de la bonne taille. Il définira également un écouteur d'animation afin qu'il puisse effacer l'indicateur d'animation une fois l'animation terminée et appellera également notifyDataSetChanged().

Enfin, avec tout cela fait, j'ai pu non seulement obtenir l'effet d'animation souhaité mais aussi la performance souhaitée car cette méthode fonctionnera avec un groupe de plus de 100 enfants.

Pour l'animation de repli, un peu plus de travail doit être fait pour que tout cela soit configuré et fonctionne. En particulier, lorsque vous remplacez onCollapse, vous ne voulez pas appeler la fonction parent car elle réduira le groupe immédiatement, vous laissant aucune chance de jouer une animation. Au lieu de cela, vous voulez appeler super.onCollapse à la fin de l'animation de repli.

MISE À JOUR:

J'ai passé du temps ce week-end à réécrire mon implémentation de cette AnimatedExpandableListView et je publie la source avec un exemple d'utilisation ici: https://github.com/idunnololz/AnimatedExpandableListView/

52
idunnololz

La solution @idunnololz fonctionne très bien. cependant je voudrais ajouter du code pour réduire le groupe précédemment développé.

private int previousGroup=-1;

    listView.setOnGroupClickListener(new OnGroupClickListener() {

        @Override
        public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
            // We call collapseGroupWithAnimation(int) and
            // expandGroupWithAnimation(int) to animate group 
            // expansion/collapse.
            if (listView.isGroupExpanded(groupPosition)) {
                listView.collapseGroupWithAnimation(groupPosition);
                previousGroup=-1;
            } else {
                listView.expandGroupWithAnimation(groupPosition);
                if(previousGroup!=-1){
                    listView.collapseGroupWithAnimation(previousGroup); 
                }
                previousGroup=groupPosition;
            }

            return true;
        }

    });
6
Hiren Dabhi
animateLayoutChanges adds auto-animation
<ExpandableListView
        Android:animateLayoutChanges="true"
        Android:layout_width="fill_parent"
        Android:layout_height="fill_parent"/>
5
KursikS

La solution @idunnololz fonctionne très bien, mais j'ai rencontré un comportement étrange avec ma disposition personnalisée pour le groupe. L'opération d'expansion n'a pas été exécutée correctement, mais l'effondrement a parfaitement fonctionné. J'ai importé son projet de test et cela a très bien fonctionné, j'ai donc réalisé que le problème venait de ma mise en page personnalisée. Cependant, lorsque je n'ai pas pu localiser le problème après une enquête, j'ai décidé de décommenter ces lignes de code dans son AnimatedExpandListView:

       if (lastGroup && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            return expandGroup(groupPos, true);
        }

qui a causé le problème (mon application est destinée à Android 4.0+).

0
vanomart