Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Collapsing CardView Animation not working correctly

What I'm trying to do

I have a RecyclerView with many items that are basically some CardView.

Those cards have a supporting text in the middle of their bodies, which has the visibility set to GONE by default, and it's made VISIBLE when I click the arrow on the right of the card.

I'm trying to animate the card while the text is revealed and while it's collapsed.

The picture below shows the expanded card and the collapsed one:

cards

The CardView layout (I've removed some parts for readability):

<android.support.v7.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardCornerRadius="3dp"
    card_view:cardElevation="4dp"
    card_view:cardUseCompatPadding="true"
    android:id="@+id/root">

    <LinearLayout
        android:id="@+id/item_ll"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/activity_vertical_margin">

        <!-- The header with the title and the item -->


        <TextView
            android:id="@+id/body_content"
            style="@style/TextAppearance.AppCompat.Medium"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:layout_marginBottom="8dp"
            android:text="@string/about_page_description"
            android:textColor="@color/secondaryText"
            android:visibility="gone"/>

        <!-- The divider, and the footer with the timestamp -->

    </LinearLayout>
</android.support.v7.widget.CardView>

The problem

The animations is working when the card is expanding and revealing the body TextView, however, when I try to collapse it back, the cards below the animated one overlaps the first one.

Example:

card animation

What I've tried so far

I've already asked a similar question about this behavior here before, but that solution is not working for a TextView in the middle of the card.

The code that's responsible for the animation part is inside the RecyclerView adapter. The arrow has a click listener that calls the method below:

private fun toggleVisibility() {
    if (bodyContent.visibility == View.GONE || bodyContent.visibility == View.INVISIBLE) {
        btSeeMore.animate().rotation(180f).start()
        TransitionManager.beginDelayedTransition(root, AutoTransition())
        bodyContent.visibility = View.VISIBLE
    }
    else {
        btSeeMore.animate().rotation(0f).start()

        TransitionManager.beginDelayedTransition(root, AutoTransition())
        bodyContent.visibility = View.GONE
    }
}

Where root is my CardView.

I've also tried to use the LinearLayout instead of the card itself for the delayed transition, but that didn't work either.

How can I achieve that behavior for my layout?

like image 983
Mauker Avatar asked Mar 01 '18 17:03

Mauker


3 Answers

You will have to perform the transition on the RecyclerView, not on individual items. Otherwise, the RecyclerView layout changes aren't taken into account by the auto transition, because it will only look at what changes in that very child view, even though in fact, other ViewHolders are indirectly affected (layout parameters are changing).

So, instead of passing "root" (the item view) to TransitionManager#beginDelayedTransition, pass a reference to your RecyclerView

like image 183
Julian Os Avatar answered Oct 22 '22 20:10

Julian Os


You have to apply TransitionManager.beginDelayedTransition on the root view where the cardview is contained

You have to remove android:animateLayoutChanges="true" from all over the layout

TransitionManager.beginDelayedTransition(the_root_view_where_card_view_exist, new AutoTransition());
like image 2
Govind Sharma Avatar answered Oct 22 '22 20:10

Govind Sharma


RecyclerView does behave oddly if his items are resizing outside RecyclerViews callbacks. Try using adapter.notifyItemChanged(position, payload) and updating the item then:

Replace adapter's onclick with this:

adapter.notifyItemChanged(adapterPosition, true) // needs adapter reference, can use more meaningful payload

Then inside of your adapter:

override fun onBindViewHolder(holder: Holder, position: Int, payloads: List<Any>) {
    if (payloads.isEmpty())
        onBindViewHolder(holder, position)
    else
        holder.toggleVisibility()
}

You can also see what happens when running delayedTransition on LinearLayout instead of Card itself.

This won't be perfect, but it will trigger animation of following items instead of them jumping and clipping.

like image 1
Pawel Avatar answered Oct 22 '22 21:10

Pawel