Articles d'expansion/d'effondrement de RecyclerView
je veux étendre/effondrer les articles de mon recyclerView afin de montrer plus d'informations. Je veux obtenir le même effet du SlideExpandableListView .
fondamentalement dans mon viseur j'ai une vue qui n'est pas visible et je veux faire une animation expansion/effondrement en douceur plutôt que de mettre la visibilité à VISIBLE/parti seulement. J'ai seulement besoin d'un élément à être élargie à un moment et ça serait cool d'avoir une certaine élévation de montrer que l'élément est sélectionné.
c'est le même effet de la nouvelle Liste Android appels récents. Les options "CALL BACK" et" DETAILS " ne sont visibles que lorsqu'un élément est sélectionné.
10 réponses
S'il vous plaît n'utilisez aucune bibliothèque pour cet effet, utilisez plutôt la façon recommandée de le faire selon Google I/O 2016. Dans votre recyclerView onBindViewHolder méthode faites ceci:
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ù détails est à mon avis sera affiché sur le toucher (les détails de l'appel dans votre cas. La Visibilité Par Défaut.DISPARU.)
- mExpandedPosition est une variable int initialisé à -1
et pour les effets cool que vous vouliez, utilisez ceux-ci comme attributs list_item:
android:background="@drawable/comment_background"
android:stateListAnimator="@animator/comment_selection"
où comment_background est:
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true"
android:enterFadeDuration="@android:integer/config_shortAnimTime"
android:exitFadeDuration="@android:integer/config_shortAnimTime">
<item android:state_activated="true" android:drawable="@color/selected_comment_background" />
<item android:drawable="@color/comment_background" />
</selector>
et comment_selection est:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true">
<objectAnimator
android:propertyName="translationZ"
android:valueTo="@dimen/z_card"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:interpolator/fast_out_slow_in" />
</item>
<item>
<objectAnimator
android:propertyName="translationZ"
android:valueTo="0dp"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:interpolator/fast_out_slow_in" />
</item>
ne pas dire que c'est la meilleure approche, mais cela semble fonctionner pour moi.
Le code complet peut être trouvé à: Exemple de code at: https://github.com/dbleicher/recyclerview-grid-quickreturn
tout d'abord, ajoutez la zone étendue à votre disposition de la cellule/élément, et faites la mise en page de la cellule qui l'entoure animateLayoutChanges="true". Cela assurera que l'expansion/l'effondrement est animé:
<LinearLayout
android:id="@+id/llCardBack"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:animateLayoutChanges="true"
android:padding="4dp"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|fill_horizontal"
android:padding="10dp"
android:gravity="center"
android:background="@android:color/holo_green_dark"
android:text="This is a long title to show wrapping of text in the view."
android:textColor="@android:color/white"
android:textSize="16sp" />
<TextView
android:id="@+id/tvSubTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center|fill_horizontal"
android:background="@android:color/holo_purple"
android:padding="6dp"
android:text="My subtitle..."
android:textColor="@android:color/white"
android:textSize="12sp" />
<LinearLayout
android:id="@+id/llExpandArea"
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:text="Item One" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="6dp"
android:text="Item Two" />
</LinearLayout>
</LinearLayout>
alors, créez votre classe D'Adaptateur RV implement View.OnClickListener de sorte que vous pouvez agir sur l'élément cliqué. Ajouter un champ int pour maintenir la position de la vue étendue, et l'initialiser à une valeur négative:
private int expandedPosition = -1;
enfin, implémentez vos méthodes ViewHolder, onBindViewHolder() et outrepassez la méthode onClick (). Vous développerez la vue dans onBindViewHolder si sa position est égale à "expandedPosition", et la masquerez si ce n'est pas le cas. Vous définissez la valeur de position étendue dans l'auditeur 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();
String theString = 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: "+theString, 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);
}
}
ceci ne devrait étendre qu'un seul élément à la fois, en utilisant l'animation par défaut du système pour le changement de layout. Au moins, ça marche pour moi. Espérons que cela aide.
pour cela, juste besoin de lignes simples pas compliqué
dans votre méthode onBindViewHolder Ajouter ci-dessous le code
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 veulent un seul article élargi et d'autres s'effondrent. Utilisez ce
d'abord déclarer une variable globale avec précédente position = -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);
}
});
fait!!!. Simple et humble .. :)
il y a une bibliothèque très simple à utiliser avec le soutien de gradle: https://github.com/cachapa/ExpandableLayout .
Droit de la bibliothèque de docs:
<net.cachapa.expandablelayout.ExpandableLinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:el_duration="1000"
app:el_expanded="true">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Click here to toggle expansion" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Fixed height"
app:layout_expandable="true" />
</net.cachapa.expandablelayout.ExpandableLinearLayout>
après avoir marqué votre extensible vues, il suffit d'appeler l'une de ces méthodes sur le conteneur: expand()
, collapse()
ou toggle()
il n'est tout simplement pas nécessaire d'utiliser des bibliothèques tierces. Un petit tweak dans la méthode démontrée dans Google I/O 2016 et Heisenberg sur ce sujet, fait l'affaire.
depuis notifyDataSetChanged()
retrace l'ensemble RecyclerView
, notifyDataItemChanged()
est une meilleure option (pas la meilleure) parce que nous avons la position et le ViewHolder
à notre disposition, et notifyDataItemChanged()
seulement retrace le particulier ViewHolder
à une position donnée .
mais le problème est que la disparition prématurée du ViewHolder
en cliquant et son émergence n'est pas éliminée même si notifyDataItemChanged()
est utilisé.
le code suivant n'est 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 titulaire de vue a un 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 s'affiche lorsqu'elle est agrandie et occultée lorsqu'elle est affaissée.
comme dit, l'élément racine de ViewHolder
est un CardView
avec foreground
et stateListAnimator
attributs définis exactement comme dit par Heisenberg sur ce sujet.
mise à JOUR: ci-dessus manifestation s'effondrera item précédemment élargi si l'un d'eux dans élargi. Pour modifier ce comportement et garder un élément étendu comme il est même quand un autre élément est étendu, 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: Lorsqu'on élargit les derniers éléments de la liste, il est possible qu'ils ne soient pas mis en pleine visibilité parce que la partie agrandie passe sous l'écran. Pour obtenir l'objet à l'intérieur de 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 la gestion de la mise en page de RecyclerView & it's (la plus tardive pour la flexibilité)
après avoir utilisé la façon recommandée de mettre en œuvre les articles extensibles/pliables résidant dans un RecyclerView
sur RecyclerView expand/collapse items répondu par HeisenBerg , j'ai vu quelques artefacts notables chaque fois que le RecyclerView
est rafraîchie en invoquant TransitionManager.beginDelayedTransition(ViewGroup)
et par la suite 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 lors de l'élément de développer/réduire
- mExpandedPosition est un
int
qui assure le suivi de l'expansion de l'élément - mExpandedHolder est un
ViewHolder
utilisé lors de l'élément de l'effondrement
Avis que la méthode TransitionManager.beginDelayedTransition(ViewGroup)
et notifyDataSetChanged()
sont remplacés par notifyItemChanged(int)
pour cibler un objet spécifique et quelques petits ajustements.
après la modification, les effets indésirables précédents devraient être partis. Cependant, ce n'est peut-être pas la solution parfaite. Il n'a fait que ce que je voulais, en éliminant les regards.
:: modifier::
par souci de clarification, mExpandedPosition
et mExpandedHolder
sont des globals.
je sais que cela fait longtemps que la question originale n'a pas été affichée. Mais je pense que pour les plus lents comme moi, un peu d'explication de la réponse de @Heisenberg serait utile.
déclarer deux variables dans la classe adaptateur comme
private int mExpandedPosition= -1;
private RecyclerView recyclerView = null;
puis dans onBindViewHolder suivant 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'objet recyclerView dans l'adaptateur override
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView = recyclerView;
}
Espérons que cette Aide.
vous pouvez utiliser ExpandableLayout. https://github.com/KyoSherlock/ExpandableLayout
cette sortie ExpandableLayout est comme une checkbox d'animation expansion/effondrement, elle peut donc être utilisée n'importe où (ListView ou RecyclerView).
après avoir réglé l'auditeur onClick à 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 comment j'ai mis en œuvre et fait le même google fait dans la vue d'appel récente.
utilisez deux types de vue dans votre RVAdapter
. Une pour la mise en page élargie et l'autre pour l'écroulement.
Et la magie se produit avec le réglage android:animateLayoutChanges="true"
pour RecyclerView
Vérifier l'effet obtenu en utilisant ce à 0: 42 dans cette vidéo