RecyclerView expandir / contraer elementos

145

Quiero expandir / contraer los elementos de mi recyclerView para mostrar más información. Quiero lograr el mismo efecto de SlideExpandableListView .

Básicamente en mi viewHolder tengo una vista que no es visible y quiero hacer una animación de expansión / colapso suave en lugar de establecer la visibilidad solo en VISIBLE / GONE. Solo necesito expandir un elemento a la vez y sería genial tener algo de elevación para mostrar que el elemento está seleccionado.

Es el mismo efecto de la nueva lista de historial de llamadas recientes de Android. Las opciones "CALL BACK" y "DETAILS" solo son visibles cuando se selecciona un elemento.

RecyclerView artículos expandibles

stanete
fuente
Tengo una pregunta relacionada. En la aplicación Reloj, la actividad del conjunto de alarma tiene una característica similar. ¿Se hace de la misma manera?
user2731584
Oye, ¿tienes la solución? También estoy buscando la misma solución para mi aplicación.
Arpit Patel
¿Encontraste alguna solución?
Armin Ghoreishi
1
He actualizado la forma recomendada de hacerlo de acuerdo con Google I / O 2016. Ver mi respuesta stackoverflow.com/questions/27203817/…
Heisenberg
mira mi respuesta aquí una solución simple y genial stackoverflow.com/a/48092441/5962715
Kiran Benny Joseph

Respuestas:

192

No use ninguna biblioteca para este efecto, en su lugar use la forma recomendada de hacerlo de acuerdo con Google I / O. En el método onBindViewHolder de su recyclerView, haga esto:

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();
    }
});
  • Donde los detalles son mi vista que se mostrará al tacto (detalles de la llamada en su caso. Visibilidad predeterminada. IDO).
  • mExpandedPosition es una variable int inicializada en -1

Y para los efectos geniales que desea, use estos como sus atributos list_item:

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

donde comment_background es:

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

y comment_selection es:

<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="2000"
        android:interpolator="@android:interpolator/fast_out_slow_in" />
</item>

<item>
    <objectAnimator
        android:propertyName="translationZ"
        android:valueTo="0dp"
        android:duration="2000"
        android:interpolator="@android:interpolator/fast_out_slow_in" />
</item>
</selector>
Heisenberg
fuente
22
Cuando se usa este código, la animación de expansión se ve rara. Cada vez que el elemento superior desaparece y todo cambia una posición. ¿Alguna vez has experimentado algo similar?
Chris-Pollux
66
Intento hacer la animación, pero implemento esto en el adaptador, pero cuál sería la solución TransitionManager.beginDelayedTransition(recyclerView);si no puedo hacer la referencia de la vista del reciclador.
Martín Serrano
2
@Ranveer Funciona más o menos. Tengo dos punto de vista de reciclaje bastante similares, pero solo funciona correctamente para uno. No tengo idea de por qué. @ MartínDaniel Debe pasar el recyclerViewparámetro como en el constructor del adaptador
chris-pollux
26
Este es un enfoque horrible. No puedo creer que se haya recomendado en Google I / O. Incluso después de investigar el proyecto Plaid e implementar todas las soluciones alternativas necesarias, la animación todavía causa algunos problemas importantes en RecyclerView, por ejemplo, el primer / último elemento visible desaparece prematuramente. También desplazarse mientras se ejecuta la animación desordena todo mal. En general, no creo que solucionar todos los problemas causados ​​por este enfoque pueda llamarse "solo ponga una línea y todo funcionará" como se anuncia.
Vitali Olshevski
66
¿Tenemos alguna forma más fresca y fácil en 2019 para este problema?
Ponomarenko Oleh
76

Para esto, solo necesitaba líneas simples no complicadas

en su método onBindViewHolder agregue el siguiente código

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 es una variable global int inicializada en -1

Para aquellos que desean que solo se expanda un elemento y que otros se colapsen. Utilizar este

primero declare una variable global con previousExpandedPosition = -1

luego

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

¡¡¡Hecho!!!. Simple y humilde .. :)

Kiran Benny Joseph
fuente
Gran solución. También recomendaría mover la configuración OnClickListeners en onCreateViewHolderlugar deonBindViewHolder
YTerle
1
Funciona muy bien, ya era hora de que hubiera una solución decente que simplemente permitiera RecyclerViewhacer el trabajo, quiero decir, ¿cuál es el sentido de lo LayoutManagercontrario si no gestiona los diseños? ¡Esta solución es mejor que la solución más votada ya que tiene menos código, permite RecyclerViewmanejar las cosas y funciona mejor!
Mark Keen
1
no entendía bien cómo agregar la vista "detalles". ¿Debería ser estático para cada titular? ¿Qué sucede si para cada elemento de la lista necesito agregar vistas diferentes?
BekaBot
@BekaBot es la vista que quieres ocultar durante el colapso del artículo
Kiran Benny Joseph
@KiranBennyJoseph sí, entiendo. En este caso, ¿la vista debería ser la misma para todos los artículos? ¿Qué sucede si quiero diferentes vistas para diferentes elementos?
BekaBot
72

No digo que este sea el mejor enfoque, pero parece funcionar para mí.

El código completo se puede encontrar en: Código de ejemplo en: https://github.com/dbleicher/recyclerview-grid-quickreturn

En primer lugar, agregue el área expandida al diseño de su celda / elemento y haga que el diseño de la celda que lo rodea animateLayoutChanges = "true". Esto asegurará que la expansión / colapso esté animada:

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

Luego, haga que su clase de Adaptador RV implemente View.OnClickListener para que pueda actuar sobre el elemento en el que se hace clic. Agregue un campo int para mantener la posición de la vista expandida e inicialícelo en un valor negativo:

private int expandedPosition = -1;

Finalmente, implemente sus métodos ViewHolder, onBindViewHolder () y anule el método onClick (). Expandirá la vista en onBindViewHolder si su posición es igual a "posición expandida" y la ocultará si no. Establece el valor de extendedPosition en el oyente 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);
    }
}

Esto debería expandir solo un elemento a la vez, utilizando la animación predeterminada del sistema para el cambio de diseño. Al menos me funciona. Espero eso ayude.

MojoTosh
fuente
Sé cómo mantener solo un elemento seleccionado. Realmente no sé cómo hacer la animación en ese elemento. La animación de expandir / contraer con la animación predeterminada del sistema no se ve muy suave.
stanete
1
Todavía no he probado lo siguiente, pero parece que funcionará. Vea la respuesta en: stackoverflow.com/a/26443762/2259418
MojoTosh
2
@ user2731584 - No lo creo. Mirando (lo que creo que es) su fuente, parece que el reloj Lollipop está usando un ListView para mostrar la lista de alarmas. Diseño: android.googlesource.com/platform/packages/apps/DeskClock/+/… Parecen tener un código personalizado para animar los elementos de expandir / contraer en esta lista. Consulte la siguiente fuente: android.googlesource.com/platform/packages/apps/DeskClock/+/…
MojoTosh
1
@MojoTosh tienes razón. Están usando ListView y están haciendo la animación usando código. expandAlarmLa función en AlarmClockFragment.javaarchivos ejecuta la parte de expansión.
user2731584
44
¿Cuál onClick()debería anular? No hay onClicken el adaptador.
Vicky Leong
15

Hay una biblioteca muy simple de usar con soporte de gradle: https://github.com/cachapa/ExpandableLayout .

Desde los documentos de la biblioteca:

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

Después de marcar sus ampliables vistas, simplemente llame a cualquiera de estos métodos en el contenedor: expand(), collapse()otoggle()

Laurențiu Onac
fuente
¿Sigue siendo la mejor manera de hacerlo? Estoy tratando de seguir lo que escribiste. Tengo algunas vistas de reciclaje que quiero expandir para mostrar un gráfico al hacer clic en
sourlemonaid
10

Sé que ha pasado mucho tiempo desde que se publicó la pregunta original. Pero creo que para los lentos como yo, un poco de explicación de la respuesta de @ Heisenberg sería útil.

Declare dos variables en la clase de adaptador como

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

Luego en onBindViewHolder siguiendo como se indica en la respuesta original.

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

Y, por último, para obtener el objeto recyclerView en la anulación del adaptador

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

    this.recyclerView = recyclerView;
}

Espero que esto ayude.

Shahroz Butt
fuente
3
Gran y sencilla solución. Pequeña mejora, use RecyclerView.NO_POSITION en lugar del número mágico -1.
Casper Bang
8

Simplemente no hay necesidad de usar bibliotecas de terceros. Un pequeño ajuste en el método demostrado en Google I / O 2016 y Heisenberg sobre este tema, es el truco.

Dado que se notifyDataSetChanged() vuelve a dibujar el completoRecyclerView , notifyDataItemChanged()es una mejor opción (no la mejor) porque tenemos la posición y el ViewHoldera nuestra disposición, y notifyDataItemChanged()solo se vuelve a dibujar lo particular ViewHolderen una posición determinada .

Pero el problema es que la desaparición prematura del ViewHolderclic y su aparición no se eliminan aunque notifyDataItemChanged()se utilicen.

El siguiente código no recurre notifyDataSetChanged()ni notifyDataItemChanged()se prueba en API 23 y funciona de maravilla cuando se usa en un RecyclerView donde cada ViewHolder tiene un CardViewelemento raíz:

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_positiones un entero global inicializado en -1. detailses la vista completa que se muestra cuando se expande y se oculta cuando se contrae.

Como se dijo, el elemento raíz de ViewHolderes un CardViewcon foregroundy stateListAnimatoratributos definidos exactamente como lo dijo Heisenberg sobre este tema.

ACTUALIZACIÓN: La demostración anterior colapsará el elemento previamente expandido si uno de ellos está expandido. Para modificar este comportamiento y mantener el elemento expandido como está, incluso cuando se expande otro elemento, necesitará el siguiente código.

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

ACTUALIZACIÓN: al expandir los últimos elementos de la lista, es posible que no se muestre completamente porque la parte expandida va debajo de la pantalla. Para obtener el elemento completo dentro de la pantalla, use el siguiente código.

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

IMPORTANTE: Para que las demostraciones anteriores funcionen, uno debe mantener en su código una instancia de RecyclerView y su LayoutManager (el último para flexibilidad)

Koushik Shom Choudhury
fuente
Gracias, esta solución me funciona. Intento evitar usarlo notifyItemChangedporque tengo una ImageViewimagen que carga desde una url. notifyItemChangedrecargar mi imagen hace que se vea raro
Tam Huynh
La línea que lo clava es TransitionManager.beginDelayedTransition(recycler);, gran descubrimiento. Pero descubrí que al usar este método, si hago clic en varias filas secuencialmente, eventualmente desencadena un bloqueo interno The specified child already has a parent. You must call removeView() on the child's parent first. Me imagino que esto se debe al reciclaje de la vista en los límites de la vista del reciclador, pero no puedo asegurarlo. ¿Alguien tuvo el mismo problema?
Roger Oba
¿Sigue siendo la mejor manera de hacerlo? Estoy tratando de seguir lo que escribiste. Tengo algunas vistas de reciclaje que quiero expandir para mostrar un gráfico cuando se hace clic en él. Me está costando entender exactamente qué hacer
sourlemonaid
¿Alguien tiene el ejemplo completo de esta implementación? Me refiero a un ejemplo de código de trabajo. Por favor ayuda. ¿No puedo entender cómo hacerlo?
Munazza
7

Después de usar la forma recomendada de implementar elementos expandibles / plegables que residen en elementos expandidos / contraídosRecyclerView en RecyclerView respondidos por HeisenBerg , he visto algunos artefactos notables cada vez que RecyclerViewse actualiza invocando TransitionManager.beginDelayedTransition(ViewGroup)y posteriormente notifyDatasetChanged().

Su respuesta original:

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

Modificado:

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());
    }
}
  • detalles es la vista que desea mostrar / ocultar durante la expansión / contracción del elemento
  • mExpandedPosition es una intque realiza un seguimiento del elemento expandido
  • mExpandedHolder se ViewHolderusa durante el colapso del elemento

Tenga en cuenta que el método TransitionManager.beginDelayedTransition(ViewGroup)y notifyDataSetChanged()se reemplazan por notifyItemChanged(int)un elemento específico de destino y algunos pequeños ajustes.

Después de la modificación, los efectos no deseados anteriores deberían desaparecer. Sin embargo, esta puede no ser la solución perfecta. Solo hizo lo que quería, eliminando los ojos.

::EDITAR::

Para aclarar, ambos mExpandedPositiony mExpandedHolderson globales.

Chan Teck Wei
fuente
Por favor, explique cómo y dónde se declara y utiliza mExpandedHolder.
Werner
1
@Werner He editado la respuesta para mostrar dónde se declara. El uso está dentro del fragmento, así como el propósito. Se utiliza para animar colapso, elemento expandido.
Chan Teck Wei
Me encanta este enfoque en lo que respecta a las animaciones, pero tengo este pequeño "desvanecimiento" al hacer clic en el elemento. ¿Tiene eso también o dicho de otra manera, esto se debe al uso notifyItemChanged ()? drive.google.com/file/d/1GaU1fcjHEfSErYF50otQGLGsIWkSyQSe/…
kazume
Si. Como habrás adivinado, es causado por notifyItemChanged(). Puede animar y cambiar manualmente la altura del elemento y agregarle vistas.
Chan Teck Wei
@ChanTeckWei: su solución funciona bien, pero la imagen de la fila parpadea cada vez que colapso o amplío la fila del elemento de la vista de reciclaje ... ¿Hay alguna solución para esto?
Pragya Mendiratta
3

Haga lo siguiente después de configurar el oyente onClick para la clase 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();
    }
}

Creo que eso debería ayudar, así es como lo implementé y hace lo mismo que Google hace en la vista de llamadas recientes.

Rensodarwin
fuente
2

Me sorprende que aún no haya una respuesta concisa, aunque una animación de expansión / contracción de este tipo es muy fácil de lograr con solo 2 líneas de código:

(recycler.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false // called once

Juntos con

notifyItemChanged(position) // in adapter, whenever a child view in item's recycler gets hidden/shown

Entonces, para mí, las explicaciones en el siguiente enlace fueron realmente útiles: https://medium.com/@nikola.jakshic/how-to-expand-collapse-items-in-recyclerview-49a648a403a6

Bianca Daniciuc
fuente
1
//Global Variable

private int selectedPosition = -1;

 @Override
    public void onBindViewHolder(final CustomViewHolder customViewHolder, final int i) {

        final int position = i;
        final GetProductCatalouge.details feedItem = this.postBeanses.get(i);
        customViewHolder.lly_main.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                selectedPosition = i;
                notifyDataSetChanged();
            }
        });
        if (selectedPosition == i) {

          if (customViewHolder.lly_hsn_code.getVisibility() == View.VISIBLE) {

            customViewHolder.lly_hsn_code.setVisibility(View.GONE);
            customViewHolder.lly_sole.setVisibility(View.GONE);
            customViewHolder.lly_sole_material.setVisibility(View.GONE);

        } else {

            customViewHolder.lly_hsn_code.setVisibility(View.VISIBLE);
            customViewHolder.lly_sole.setVisibility(View.VISIBLE);
            customViewHolder.lly_sole_material.setVisibility(View.VISIBLE);
        }


        } else {
            customViewHolder.lly_hsn_code.setVisibility(View.GONE);
            customViewHolder.lly_sole.setVisibility(View.GONE);
            customViewHolder.lly_sole_material.setVisibility(View.GONE);
        }
}

ingrese la descripción de la imagen aquí

Keshav Gera
fuente
0

Use dos tipos de vista en su RVAdapter. Uno para el diseño expandido y otro para contraído. Y la magia sucede con la configuración android:animateLayoutChanges="true"de RecyclerView Checkout, el efecto logrado usando esto a las 0:42 en este video

penduDev
fuente
¿Te importaría explicar esto mejor? Parece que no puedo hacerlo funcionar
sourlemonaid
@sourlemonaid básicamente crea múltiples tipos de vista para la lista como se explica en stackoverflow.com/a/26245463/3731795 ... Mantiene un seguimiento del índice para el que se expande el elemento, y el resto de los elementos se colapsan en ese momento . Ahora si (current_item_index == extended_item_index) {return VIEW_TYPE_EXPANDED} else {reutrn VIEW_TYPE_COLLAPSED}
penduDev