RecyclerView rozwiń / zwiń elementy

145

Chcę rozwinąć / zwinąć elementy mojego widoku recyclingerView, aby wyświetlić więcej informacji. Chcę osiągnąć ten sam efekt, co SlideExpandableListView .

Zasadniczo w moim ViewHolder mam widok, który nie jest widoczny i chcę zrobić płynną animację rozwijania / zwijania, zamiast ustawiać widoczność na VISIBLE / GONE. Potrzebuję tylko elementu do rozwinięcia naraz i fajnie byłoby mieć jakieś podniesienie, aby pokazać, że element jest wybrany.

Jest to ten sam efekt, co nowa lista historii ostatnich połączeń Androida. Opcje „ODDZWOŃ” i „SZCZEGÓŁY” są widoczne tylko wtedy, gdy element jest zaznaczony.

Elementy rozwijane RecyclerView

stanete
źródło
Mam podobne pytanie. W aplikacji Zegar aktywność zestawu alarmów ma podobną funkcję. Czy robi się to w ten sam sposób?
user2731584
Hej, masz rozwiązanie? szukam również tego samego rozwiązania dla mojej aplikacji.
Arpit Patel
Czy znalazłeś jakieś rozwiązanie?
Armin Ghoreishi
1
Zaktualizowałem zalecany sposób robienia tego zgodnie z Google I / O 2016. Zobacz moją odpowiedź stackoverflow.com/questions/27203817/ ...
Heisenberg
sprawdź moją odpowiedź tutaj proste i fajne rozwiązanie stackoverflow.com/a/48092441/5962715
Kiran Benny Joseph

Odpowiedzi:

192

Nie używaj żadnej biblioteki do tego efektu, zamiast tego użyj zalecanego sposobu robienia tego zgodnie z Google I / O. W metodzie onBindViewHolder w Twoim ecclerView wykonaj następujące czynności:

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();
    }
});
  • Gdzie szczegóły to mój widok, który będzie wyświetlany po dotknięciu (szczegóły połączenia w twoim przypadku. Domyślna widoczność.GONE).
  • mExpandedPosition to zmienna typu int zainicjowana na -1

Aby uzyskać fajne efekty, które chciałeś, użyj tych jako atrybutów list_item:

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

gdzie comment_background to:

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

a comment_selection to:

<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
źródło
22
Podczas korzystania z tego kodu animacja rozwijania wygląda dziwnie. Za każdym razem, gdy najwyższy element znika i wszystko przesuwa się w górę o jedną pozycję. Czy kiedykolwiek doświadczyłeś czegoś podobnego?
chris-pollux
6
Próbuję wykonać animację, ale implementuję ją na adapterze, ale jakie byłoby rozwiązanie, TransitionManager.beginDelayedTransition(recyclerView);jeśli nie mogę znaleźć odniesienia do widoku recyklera.
Martín Serrano
2
@Ranveer To trochę działa. Mam dwa dość podobne widoki recyklingu, ale działa poprawnie tylko dla jednego. Nie mam pojęcia dlaczego. @ MartínDaniel Musisz przekazać recyclerViewjako parametr w konstruktorze adaptera
chris-pollux
26
To okropne podejście. Nie mogę uwierzyć, że zostało to polecone na Google I / O. Nawet po zbadaniu projektu Plaid i wdrożeniu wszystkich wymaganych obejść, animacja nadal powoduje kilka poważnych usterek w RecyclerView, np. Przedwczesne zniknięcie pierwszego / ostatniego widocznego elementu. Również przewijanie, gdy animacja działa, źle wszystko psuje. Ogólnie rzecz biorąc, nie sądzę, aby naprawianie wszystkich problemów spowodowanych przez to podejście można było nazwać „po prostu wstaw jedną linię i wszystko będzie działać”, jak reklamowano.
Vitali Olshevski
6
Czy w 2019 roku mamy bardziej świeży i łatwy sposób na rozwiązanie tego problemu?
Ponomarenko Oleh
76

Do tego potrzebne były tylko proste, a nie skomplikowane linie

w swojej metodzie onBindViewHolder dodaj poniższy kod

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 to zmienna globalna int zainicjowana na -1

Dla tych, którzy chcą rozwinąć tylko jeden element, a inne zwinąć. Użyj tego

najpierw zadeklaruj zmienną globalną z previousExpandedPosition = -1

następnie

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

Gotowe!!!. Prosty i pokorny .. :)

Kiran Benny Joseph
źródło
świetne rozwiązanie. OnClickListeneronCreateViewHolderonBindViewHolder
Poleciłbym
1
Działa bardzo dobrze, najwyższy czas pojawiło się przyzwoite rozwiązanie, które po prostu pozwala RecyclerViewpracować, to znaczy, jaki jest sens w LayoutManagerprzeciwnym razie, jeśli nie zarządza układami! To rozwiązanie jest lepsze niż rozwiązanie z największą liczbą głosów, ponieważ ma mniej kodu, pozwala na RecyclerViewobsługę i działa lepiej!
Mark Keen
1
nie bardzo rozumiem, jak dodać widok „szczegółów”. Czy powinien być statyczny dla każdego uchwytu? A jeśli do każdego elementu listy muszę dodać różne widoki?
BekaBot
@BekaBot to widok, który chcesz ukryć podczas zwijania elementu
Kiran Benny Joseph
@KiranBennyJoseph tak, rozumiem. W takim przypadku widok powinien być taki sam dla wszystkich elementów? A jeśli chcę mieć różne widoki dla różnych przedmiotów?
BekaBot
72

Nie mówię, że to najlepsze podejście, ale wydaje mi się, że działa.

Pełny kod można znaleźć pod adresem: Przykładowy kod pod adresem : https://github.com/dbleicher/recyclerview-grid-quickreturn

Po pierwsze, dodaj rozszerzony obszar do układu komórki / elementu i ustaw otaczający układ komórek animateLayoutChanges = "true". Zapewni to animację rozwijania / zwijania:

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

Następnie spraw, aby klasa RV Adapter implementowała View.OnClickListener, tak aby można było działać na klikanym elemencie. Dodaj pole int, aby zachować pozycję jednego rozwiniętego widoku i zainicjuj go do wartości ujemnej:

private int expandedPosition = -1;

Na koniec zaimplementuj metody ViewHolder, onBindViewHolder () i zastąp metodę onClick (). Rozszerzysz widok w onBindViewHolder, jeśli jego pozycja jest równa „extendedPosition”, a jeśli nie, to ukryjesz. Ustawiasz wartość extendedPosition w odbiorniku 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);
    }
}

Powinno to rozwinąć tylko jeden element naraz, używając domyślnej animacji systemu do zmiany układu. Przynajmniej dla mnie to działa. Mam nadzieję, że to pomoże.

MojoTosh
źródło
Wiem, jak zaznaczyć tylko jedną pozycję. Naprawdę nie wiem, jak zrobić animację tego przedmiotu. Animacja rozwijania / zwijania z domyślną animacją systemową nie wygląda zbyt płynnie.
stanete
1
Nie próbowałem jeszcze poniższych rozwiązań, ale na pewno wygląda na to, że zadziałają. Sprawdź odpowiedź pod adresem: stackoverflow.com/a/26443762/2259418
MojoTosh,
2
@ user2731584 - Nie sądzę. Patrząc na (jak sądzę) ich źródło, wydaje się, że zegar Lollipop używa ListView do wyświetlania listy alarmów. Układ: android.googlesource.com/platform/packages/apps/DeskClock/+/… Wydaje się, że mają niestandardowy kod do animacji rozwijania / zwijania elementów na tej liście. Zobacz następujące źródło: android.googlesource.com/platform/packages/apps/DeskClock/+/…
MojoTosh
1
@MojoTosh masz rację. Używają ListView i wykonują animację za pomocą kodu. expandAlarmFunkcja w AlarmClockFragment.javaplikach wykonuje część rozszerzającą.
user2731584
4
Który onClick() powinienem zastąpić? Nie ma onClickw adapterze.
Vicky Leong
15

Istnieje bardzo prosta w użyciu biblioteka z obsługą gradle: https://github.com/cachapa/ExpandableLayout .

Bezpośrednio z dokumentów biblioteki:

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

Po oznaczyć rozszerzalne widoki, po prostu zadzwoń do żadnej z tych metod na pojemniku: expand(), collapse()lubtoggle()

Laurențiu Onac
źródło
Czy nadal jest to najlepszy sposób, aby to zrobić? Staram się śledzić to, co napisałeś. Mam kilka widoków z recyklingu, które chcę rozwinąć, aby po kliknięciu wyświetlić wykres
sourlemonaid
10

Wiem, że minęło dużo czasu od opublikowania pierwotnego pytania. Ale myślę, że w przypadku powolnych, takich jak ja, pomocne byłoby wyjaśnienie odpowiedzi @ Heisenberga.

Zadeklaruj dwie zmienne w klasie adaptera jako

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

Następnie w onBindViewHolder, jak podano w oryginalnej odpowiedzi.

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

I na koniec, aby uzyskać obiekt recyclinglerView w przesłonięciu adaptera

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

    this.recyclerView = recyclerView;
}

Mam nadzieję że to pomoże.

tyłek shahroz
źródło
3
Świetne i proste rozwiązanie. Mała poprawa, użyj RecyclerView.NO_POSITION zamiast magicznej liczby -1.
Casper Bang,
8

Po prostu nie ma potrzeby korzystania z bibliotek innych firm. Trochę uszczypnąć w metodzie wykazano w Google I / O 2016 i Heisenberga na ten temat, załatwia sprawę.

Ponieważ notifyDataSetChanged() przerysowuje w całościRecyclerView , notifyDataItemChanged()jest lepszą opcją (nie najlepszą), ponieważ mamy pozycję i do ViewHolderdyspozycji, a notifyDataItemChanged()tylko przerysowuje konkret ViewHolderna danej pozycji .

Problem polega jednak na tym, że przedwczesne zniknięcie ViewHolderpo kliknięciu i jego pojawienie się nie jest wyeliminowane, nawet jeśli notifyDataItemChanged()jest używane.

Poniższy kod nie odwołuje się do notifyDataSetChanged()ani notifyDataItemChanged()i jest testowany na interfejsie API 23 i działa jak urok, gdy jest używany w RecyclerView, gdzie każdy element ViewHolder ma CardViewjako element główny:

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_positionjest globalną liczbą całkowitą zainicjowaną na -1. detailsto pełny widok, który jest wyświetlany po rozwinięciu i zasłonięciu po zwinięciu.

Jak powiedziano, głównym elementem ViewHolderjest atrybut a CardViewwith foregroundi stateListAnimatorzdefiniowane dokładnie tak, jak powiedział Heisenberg na ten temat.

AKTUALIZACJA: Powyższa demonstracja zwinie wcześniej rozwinięty element, jeśli jeden z nich zostanie rozwinięty. Aby zmodyfikować to zachowanie i zachować rozwinięty element w takim stanie, w jakim jest nawet po rozwinięciu innego elementu, potrzebujesz następującego kodu.

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

AKTUALIZACJA: Podczas rozwijania ostatnich elementów na liście, może nie być ona w pełni widoczna, ponieważ rozwinięta część znajduje się poniżej ekranu. Aby uzyskać pełną pozycję na ekranie, użyj następującego kodu.

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

WAŻNE: Aby powyższe demonstracje działały, należy zachować w swoim kodzie wystąpienie RecyclerView i jego LayoutManager (później dla elastyczności)

Koushik Shom Choudhury
źródło
Dzięki temu to rozwiązanie działa u mnie. Staram się unikać używania, notifyItemChangedponieważ mam ImageViewzaładowany obraz z adresu URL. notifyItemChangedprzeładuj mój obraz sprawia, że ​​wygląda dziwnie
Tam Huynh
Linia, która go przybija TransitionManager.beginDelayedTransition(recycler);, świetne znalezisko. Ale odkryłem, że używając tej metody, jeśli kliknę spamowo kilka wierszy po kolei, w końcu wywołuje to wewnętrzną awarię The specified child already has a parent. You must call removeView() on the child's parent first. Wyobrażam sobie, że jest to spowodowane recyklingiem widoku na granicach widoku recyklera, ale nie mogę tego powiedzieć na pewno. Czy ktoś miał ten sam problem?
Roger Oba
Czy nadal jest to najlepszy sposób, aby to zrobić? Staram się śledzić to, co napisałeś. Mam kilka widoków recyklingu, które chcę rozwinąć, aby po kliknięciu wyświetlić wykres. Trudno mi zrozumieć, co mam robić
sourlemonaid
Czy ktoś ma pełny przykład tej realizacji? Mam na myśli działający przykład kodu. Proszę pomóż. Nie rozumiem, jak to zrobić?
Munazza
7

Po zastosowaniu zalecanego sposobu implementacji elementów rozwijanych / zwijanych znajdujących się w elementach rozwijania / zwijania w RecyclerView,RecyclerView na które odpowiedziała HeisenBerg , zauważyłem zauważalne artefakty za każdym razem, gdy odświeżany jest przez wywołanieRecyclerViewTransitionManager.beginDelayedTransition(ViewGroup) a następnie notifyDatasetChanged().

Jego oryginalna odpowiedź:

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

Zmodyfikowano:

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());
    }
}
  • Detale to widok, który chcesz pokazać / ukryć podczas rozwijania / zwijania elementu
  • mExpandedPosition to narzędzie, intktóre śledzi rozwinięty element
  • mExpandedHolder to plikViewHolder używany podczas zwijania elementu

Zwróć uwagę, że metoda TransitionManager.beginDelayedTransition(ViewGroup)inotifyDataSetChanged() są zastępowane przez notifyItemChanged(int)celowanie w określony przedmiot i kilka drobnych poprawek.

Po modyfikacji poprzednie niechciane efekty powinny zniknąć. Jednak może to nie być idealne rozwiązanie. Zrobił tylko to, co chciałem, eliminując bóle oczu.

::EDYTOWAĆ::

Dla wyjaśnienia, oba mExpandedPositioni mExpandedHoldersą globalne.

Chan Teck Wei
źródło
Proszę wyjaśnić, jak i gdzie mExpandedHolder jest zadeklarowany i używany?
Werner,
1
@Werner Zmieniłem odpowiedź, aby pokazać, gdzie jest zadeklarowana. Użycie znajduje się w samym fragmencie, a także w celu. Służy do animowania zwijanego, rozwiniętego elementu.
Chan Teck Wei
Najbardziej podoba mi się to podejście, jeśli chodzi o animacje, ale po kliknięciu elementu mam to małe „zanikanie”. Czy masz to również, czy inaczej, czy jest to spowodowane użyciem notifyItemChanged ()? drive.google.com/file/d/1GaU1fcjHEfSErYF50otQGLGsIWkSyQSe/ ...
kazume
Tak. Jak się domyślasz, jest to spowodowane przez notifyItemChanged(). Możesz ręcznie animować i zmieniać wysokość elementu oraz dodawać do niego widoki.
Chan Teck Wei
@ChanTeckWei: Twoje rozwiązanie działa dobrze, ale obraz w wierszu migocze za każdym razem, gdy zwijam lub rozszerzam wiersz pozycji z widokiem recyklingu. Czy jest na to jakieś rozwiązanie?
Pragya Mendiratta,
3

Wykonaj następujące czynności po ustawieniu odbiornika onClick na klasę 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();
    }
}

Myślę, że to powinno pomóc, tak właśnie wdrożyłem i robi to samo, co Google w widoku ostatnich połączeń.

Rensodarwin
źródło
2

Dziwię się, że nie ma jeszcze zwięzłej odpowiedzi, chociaż taka animacja rozwijania / zwijania jest bardzo łatwa do osiągnięcia za pomocą zaledwie 2 linii kodu:

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

razem z

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

Więc dla mnie wyjaśnienia w linku poniżej były naprawdę przydatne: https://medium.com/@nikola.jakshic/how-to-expand-collapse-items-in-recyclerview-49a648a403a6

Bianca Daniciuc
źródło
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);
        }
}

wprowadź opis obrazu tutaj

Keshav Gera
źródło
0

Użyj dwóch typów widoku w pliku RVAdapter. Jeden dla rozwiniętego układu, a drugi dla zwiniętego. I magia dzieje się z ustawieniem android:animateLayoutChanges="true"dla RecyclerView Checkout efektu osiągniętego przy użyciu tego w tym filmie przy 0:42

penduDev
źródło
masz ochotę wyjaśnić to lepiej? Nie wydaje mi się, żeby to działało
sourlemonaid
@sourlemonaid zasadniczo tworzysz wiele typów widoków dla listy, jak wyjaśniono w stackoverflow.com/a/26245463/3731795 ... Śledzisz indeks, dla którego element jest rozwijany, a pozostałe elementy są w tym czasie zwinięte . Teraz if (current_item_index == extended_item_index) {return VIEW_TYPE_EXPANDED} else {reutrn VIEW_TYPE_COLLAPSED}
penduDev