RecyclerView expand / collapse items

Je veux agrandir / réduire les éléments de mon recyclerView pour afficher plus d’informations. Je veux obtenir le même effet que SlideExpandableListView .

Fondamentalement, dans mon viewHolder, j’ai une vue qui n’est pas visible et je veux faire une animation d’expansion / réduction en douceur au lieu de définir uniquement la visibilité VISIBLE / GONE. Je n’ai besoin que d’un élément à agrandir à la fois et ce serait cool d’avoir une certaine élévation pour montrer que l’élément est sélectionné.

C’est le même effet de la nouvelle liste d’historique d’appels récents d’Android. Les options “RAPPEL” et “DÉTAILS” ne sont visibles que lorsqu’un élément est sélectionné.

RecyclerView articles extensibles

    Veuillez ne pas utiliser de bibliothèque pour cet effet. Utilisez plutôt la méthode recommandée selon Google I / O 2016. Dans la méthode onBindViewHolder de votre recyclerView, procédez comme suit :

    final boolean isExpanded = position==mExpandedPosition; holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE); holder.itemView.setActivated(isExpanded); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mExpandedPosition = isExpanded ? -1:position; TransitionManager.beginDelayedTransition(recyclerView); notifyDataSetChanged(); } }); 
    • Où les détails sont ma vue qui seront affichés au toucher (détails de l’appel dans votre cas. Default Visibility.GONE).
    • mExpandedPosition est une variable int initialisée à -1

    Et pour les effets sympas que vous vouliez, utilisez-les comme atsortingbuts list_item:

     android:background="@drawable/comment_background" android:stateListAnimator="@animator/comment_selection" 

    où comment_background est:

         

    et comment_selection est:

            

    Ne pas dire que c’est la meilleure approche, mais cela semble fonctionner pour moi.

    Le code complet peut être trouvé à: Exemple de code à l’ adresse : https://github.com/dbleicher/recyclerview-grid-quickreturn

    Tout d’abord, ajoutez la zone développée à la disposition de votre cellule / élément et définissez la disposition de la cellule englobante animateLayoutChanges = “true”. Cela garantira que l’expansion / la réduction est animée:

             

    Ensuite, faites en sorte que votre classe d’adaptateur RV implémente View.OnClickListener afin que vous puissiez agir sur l’élément sur lequel vous cliquez. Ajoutez un champ int pour conserver la position de la vue étendue et initialisez-la à une valeur négative:

     private int expandedPosition = -1; 

    Enfin, implémentez vos méthodes ViewHolder, onBindViewHolder () et remplacez la méthode onClick (). Vous allez développer la vue dans onBindViewHolder si sa position est égale à “extendedPosition”, et le masquer sinon. Vous définissez la valeur de expandPosition dans le programme d’écoute onClick:

     @Override public void onBindViewHolder(RVAdapter.ViewHolder holder, int position) { int colorIndex = randy.nextInt(bgColors.length); holder.tvTitle.setText(mDataset.get(position)); holder.tvTitle.setBackgroundColor(bgColors[colorIndex]); holder.tvSubTitle.setBackgroundColor(sbgColors[colorIndex]); if (position == expandedPosition) { holder.llExpandArea.setVisibility(View.VISIBLE); } else { holder.llExpandArea.setVisibility(View.GONE); } } @Override public void onClick(View view) { ViewHolder holder = (ViewHolder) view.getTag(); Ssortingng theSsortingng = mDataset.get(holder.getPosition()); // Check for an expanded view, collapse if you find one if (expandedPosition >= 0) { int prev = expandedPosition; notifyItemChanged(prev); } // Set the current position to "expanded" expandedPosition = holder.getPosition(); notifyItemChanged(expandedPosition); Toast.makeText(mContext, "Clicked: "+theSsortingng, Toast.LENGTH_SHORT).show(); } /** * Create a ViewHolder to represent your cell layout * and data element structure */ public static class ViewHolder extends RecyclerView.ViewHolder { TextView tvTitle; TextView tvSubTitle; LinearLayout llExpandArea; public ViewHolder(View itemView) { super(itemView); tvTitle = (TextView) itemView.findViewById(R.id.tvTitle); tvSubTitle = (TextView) itemView.findViewById(R.id.tvSubTitle); llExpandArea = (LinearLayout) itemView.findViewById(R.id.llExpandArea); } } 

    Cela devrait développer un seul élément à la fois, en utilisant l’animation par défaut du système pour la modification de la disposition. Au moins ça marche pour moi. J’espère que cela aide.

    Pour cela, il suffit de simples lignes pas compliquées

    dans votre méthode onBindViewHolder, ajoutez le code ci-dessous

     final boolean isExpanded = position==mExpandedPosition; holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE); holder.itemView.setActivated(isExpanded); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mExpandedPosition = isExpanded ? -1:position; notifyItemChanged(position); } }); 

    mExpandedPosition est une variable globale int initialisée à -1

    Pour ceux qui ne veulent qu’un seul article et que d’autres s’effondrent. Utilisez ceci

    déclarez d’abord une variable globale avec previousExpandedPosition = -1

    puis

      final boolean isExpanded = position==mExpandedPosition; holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE); holder.itemView.setActivated(isExpanded); if (isExpanded) previousExpandedPosition = position; holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mExpandedPosition = isExpanded ? -1:position; notifyItemChanged(previousExpandedPosition); notifyItemChanged(position); } }); 

    Terminé!!!. Simple et humble .. 🙂

    Il existe une bibliothèque très simple à utiliser avec le support de gradle: https://github.com/cachapa/ExpandableLayout .

    À partir des documents de la bibliothèque:

         

    Après avoir marqué vos vues extensibles , appelez l’une de ces méthodes sur le conteneur: expand() , collapse() ou toggle()

    Vous pouvez utiliser ExpandableLayout. https://github.com/KyoSherlock/ExpandableLayout

    Cette ExpandableLayout est comme une animation CheckBox d’extension / réduction en douceur, de sorte qu’elle peut être utilisée n’importe où (ListView ou RecyclerView).

    Je sais que cela fait longtemps que la question initiale a été publiée. Mais je pense que pour les plus lents comme moi, une explication de la réponse de @ Heisenberg serait utile.

    Déclarez deux variables dans la classe d’adaptateur comme

     private int mExpandedPosition= -1; private RecyclerView recyclerView = null; 

    Puis dans onBindViewHolder comme indiqué dans la réponse originale.

      // This line checks if the item displayed on screen // was expanded or not (Remembering the fact that Recycler View ) // reuses views so onBindViewHolder will be called for all // items visible on screen. final boolean isExpanded = position==mExpandedPosition; //This line hides or shows the layout in question holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE); // I do not know what the heck this is :) holder.itemView.setActivated(isExpanded); // Click event for each item (itemView is an in-built variable of holder class) holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // if the clicked item is already expaned then return -1 //else return the position (this works with notifyDatasetchanged ) mExpandedPosition = isExpanded ? -1:position; // fancy animations can skip if like TransitionManager.beginDelayedTransition(recyclerView); //This will call the onBindViewHolder for all the itemViews on Screen notifyDataSetChanged(); } }); 

    Et enfin pour obtenir l’object recyclerView dans le remplacement de l’adaptateur

     @Override public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); this.recyclerView = recyclerView; } 

    J’espère que cela t’aides.

    Procédez comme suit après avoir défini l’écouteur onClick sur la classe ViewHolder:

     @Override public void onClick(View v) { final int originalHeight = yourLinearLayout.getHeight(); animationDown(YourLinearLayout, originalHeight);//here put the name of you layout that have the options to expand. } //Animation for devices with kitkat and below public void animationDown(LinearLayout billChoices, int originalHeight){ // Declare a ValueAnimator object ValueAnimator valueAnimator; if (!billChoices.isShown()) { billChoices.setVisibility(View.VISIBLE); billChoices.setEnabled(true); valueAnimator = ValueAnimator.ofInt(0, originalHeight+originalHeight); // These values in this method can be changed to expand however much you like } else { valueAnimator = ValueAnimator.ofInt(originalHeight+originalHeight, 0); Animation a = new AlphaAnimation(1.00f, 0.00f); // Fade out a.setDuration(200); // Set a listener to the animation and configure onAnimationEnd a.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { billChoices.setVisibility(View.INVISIBLE); billChoices.setEnabled(false); } @Override public void onAnimationRepeat(Animation animation) { } }); // Set the animation on the custom view billChoices.startAnimation(a); } valueAnimator.setDuration(200); valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { Integer value = (Integer) animation.getAnimatedValue(); billChoices.getLayoutParams().height = value.intValue(); billChoices.requestLayout(); } }); valueAnimator.start(); } } 

    Je pense que cela devrait aider, c’est comme ça que j’ai implémenté et fait le même Google dans la vue des appels récents.

    Après avoir utilisé la méthode recommandée pour implémenter des éléments extensibles / réductibles résidant dans RecyclerView sur les éléments de développement / réduction RecyclerView auxquels HeisenBerg a répondu, j’ai vu des artefacts visibles à chaque actualisation de RecyclerView en TransitionManager.beginDelayedTransition(ViewGroup) notifyDatasetChanged() .

    Sa réponse originale:

     final boolean isExpanded = position==mExpandedPosition; holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE); holder.itemView.setActivated(isExpanded); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mExpandedPosition = isExpanded ? -1 : position; TransitionManager.beginDelayedTransition(recyclerView); notifyDataSetChanged(); } }); 

    Modifié:

     final boolean isExpanded = position == mExpandedPosition; holder.details.setVisibility(isExpanded ? View.VISIBLE : View.GONE); holder.view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mExpandedHolder != null) { mExpandedHolder.details.setVisibility(View.GONE); notifyItemChanged(mExpandedPosition); } mExpandedPosition = isExpanded ? -1 : holder.getAdapterPosition(); mExpandedHolder = isExpanded ? null : holder; notifyItemChanged(holder.getAdapterPosition()); } } 
    • détails est la vue que vous souhaitez afficher / masquer pendant l’élément
    • mExpandedPosition est un int qui garde trace de l’élément développé
    • mExpandedHolder est un ViewHolder utilisé lors de la ViewHolder éléments

    Notez que la méthode TransitionManager.beginDelayedTransition(ViewGroup) et notifyDataSetChanged() sont remplacées par notifyItemChanged(int) pour cibler un élément spécifique et quelques petites modifications.

    Après la modification, les effets indésirables précédents doivent disparaître. Cependant, cela peut ne pas être la solution parfaite. Cela n’a fait que ce que je voulais, en éliminant les regards.

    ::MODIFIER::

    Pour plus de clarté, mExpandedPosition et mExpandedHolder sont des globals.

    Il n’est tout simplement pas nécessaire d’utiliser des bibliothèques tierces. Un petit changement dans la méthode démontrée dans Google I / O 2016 et Heisenberg sur ce sujet, fait l’affaire.

    Puisque notifyDataSetChanged() redessine le RecyclerView complet , notifyDataItemChanged() est une meilleure option (pas la meilleure) car nous avons la position et le ViewHolder à notre disposition et notifyDataItemChanged() redessine uniquement le ViewHolder particulier à une position donnée .

    Mais le problème est que la disparition prématurée du ViewHolder lors du clic et de son émergence n’est pas éliminée même si notifyDataItemChanged() est utilisé.

    Le code suivant n’a pas recours à notifyDataSetChanged() ou notifyDataItemChanged() et est testé sur API 23 et fonctionne comme un charme lorsqu’il est utilisé sur un RecyclerView où chaque ViewHolder possède un object CardView comme élément racine:

     holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final boolean visibility = holder.details.getVisibility()==View.VISIBLE; if (!visibility) { holder.itemView.setActivated(true); holder.details.setVisibility(View.VISIBLE); if (prev_expanded!=-1 && prev_expanded!=position) { recycler.findViewHolderForLayoutPosition(prev_expanded).itemView.setActivated(false); recycler.findViewHolderForLayoutPosition(prev_expanded).itemView.findViewById(R.id.cpl_details).setVisibility(View.GONE); } prev_expanded = position; } else { holder.itemView.setActivated(false); holder.details.setVisibility(View.GONE); } TransitionManager.beginDelayedTransition(recycler); } }); 

    prev_position est un entier global initialisé à -1. details est la vue complète qui est affichée lorsqu’elle est agrandie et masquée lorsqu’elle est réduite.

    Comme indiqué ViewHolder , l’élément racine de ViewHolder est un CardView avec les atsortingbuts foreground et stateListAnimator définis exactement comme dit par Heisenberg sur ce sujet.

    MISE À JOUR: La démonstration ci-dessus réduira l’article déjà étendu si l’un d’eux est développé. Pour modifier ce comportement et conserver l’élément tel qu’il est même lorsqu’un autre élément est développé, vous aurez besoin du code suivant.

     if (row.details.getVisibility()!=View.VISIBLE) { row.details.setVisibility(View.VISIBLE); row.root.setActivated(true); row.details.animate().alpha(1).setStartDelay(500); } else { row.root.setActivated(false); row.details.setVisibility(View.GONE); row.details.setAlpha(0); } TransitionManager.beginDelayedTransition(recycler); 

    MISE À JOUR: Lors de l’élargissement des derniers éléments de la liste, il est possible qu’elle ne soit pas totalement visible car la partie développée passe sous l’écran. Pour obtenir l’élément complet dans l’écran, utilisez le code suivant.

     LinearLayoutManager manager = (LinearLayoutManager) recycler.getLayoutManager(); int distance; View first = recycler.getChildAt(0); int height = first.getHeight(); int current = recycler.getChildAdapterPosition(first); int p = Math.abs(position - current); if (p > 5) distance = (p - (p - 5)) * height; else distance = p * height; manager.scrollToPositionWithOffset(position, distance); 

    IMPORTANT: Pour que les démonstrations ci-dessus fonctionnent, il faut conserver dans leur code une instance de RecyclerView & it’s LayoutManager (la plus tard pour plus de flexibilité)

    Utilisez deux types de vue dans votre RVAdapter . Un pour l’agencement étendu et un autre pour celui qui est réduit. Et la magie se produit avec la configuration de android:animateLayoutChanges="true" pour RecyclerView Checkout l’effet obtenu en utilisant ceci à 0:42 dans cette vidéo