You can use Transition or Animator that changes visibility of section to be expanded/collapsed, or ConstraintSet with different layouts.

Easiest one is to use motionLayout with 2 different layouts and constraintSets to change from one layout to another on button click. You can change between layouts with
val constraintSet = ConstraintSet() constraintSet.clone(this, R.layout.layout_collapsed) val transition = ChangeBounds() transition.interpolator = AccelerateInterpolator(1.0f) transition.setDuration(300) TransitionManager.beginDelayedTransition(YOUR_VIEW, transition) constraintSet.applyTo(YOUR_VIEW)
With Transition api
RotateX.kt
I created the one in gif using Transitions api that change rotationX.
class RotateX : Transition { @Keep constructor() : super() @Keep constructor(context: Context, attrs: AttributeSet) : super(context, attrs) override fun getTransitionProperties(): Array<String> { return TRANSITION_PROPERTIES } override fun captureStartValues(transitionValues: TransitionValues) { captureValues(transitionValues) } override fun captureEndValues(transitionValues: TransitionValues) { captureValues(transitionValues) } override fun createAnimator( sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues? ): Animator? { if (startValues == null || endValues == null) return null val startRotation = startValues.values[PROP_ROTATION] as Float val endRotation = endValues.values[PROP_ROTATION] as Float if (startRotation == endRotation) return null val view = endValues.view // ensure the pivot is set view.pivotX = view.width / 2f view.pivotY = view.height / 2f return ObjectAnimator.ofFloat(view, View.ROTATION_X, startRotation, endRotation) } private fun captureValues(transitionValues: TransitionValues) { val view = transitionValues.view if (view == null || view.width <= 0 || view.height <= 0) return transitionValues.values[PROP_ROTATION] = view.rotationX } companion object { private const val PROP_ROTATION = "iosched:rotate:rotation" private val TRANSITION_PROPERTIES = arrayOf(PROP_ROTATION) } }
create xml file that targets expand button
<?xml version="1.0" encoding="utf-8"?> <transitionSet xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:interpolator/fast_out_slow_in"> <transition class="com.smarttoolfactory.tutorial3_1transitions.transition.RotateX"> <targets> <target android:targetId="@id/ivExpand" /> </targets> </transition> <autoTransition android:duration="200" /> </transitionSet> My layout to be expanded or collapsed <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <com.google.android.material.card.MaterialCardView android:id="@+id/cardView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="4dp" android:layout_marginVertical="2dp" android:clickable="true" android:focusable="true" android:transitionName="@string/transition_card_view" app:cardCornerRadius="0dp" app:cardElevation="0dp" app:cardPreventCornerOverlap="false"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="16dp" android:paddingBottom="16dp"> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/ivAvatar" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:scaleType="centerCrop" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:src="@drawable/avatar_1_raster" /> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/ivExpand" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:padding="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/ic_baseline_expand_more_24" /> <TextView android:id="@+id/tvTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="12dp" android:layout_marginTop="6dp" android:text="Some Title" android:textSize="20sp" android:textStyle="bold" app:layout_constraintStart_toEndOf="@+id/ivAvatar" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tvDate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:textColor="?android:textColorSecondary" android:textSize="12sp" app:layout_constraintStart_toStartOf="@+id/tvTitle" app:layout_constraintTop_toBottomOf="@id/tvTitle" tools:text="Tuesday 7pm" /> <TextView android:id="@+id/tvBody" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:ellipsize="end" android:lines="1" android:text="@string/bacon_ipsum_short" android:textSize="16sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/ivAvatar" app:layout_constraintTop_toBottomOf="@id/tvDate" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:orientation="horizontal" android:overScrollMode="never" android:visibility="gone" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvBody" tools:listitem="@layout/item_image_destination" /> </androidx.constraintlayout.widget.ConstraintLayout> </com.google.android.material.card.MaterialCardView> </layout>
And set up visibility of items to collapse or expand
private fun setUpExpandedStatus() { if (isExpanded) { binding.recyclerView.visibility = View.VISIBLE binding.ivExpand.rotationX = 180f } else { binding.recyclerView.visibility = View.GONE binding.ivExpand.rotationX = 0f } }
And start transition with
val transition = TransitionInflater.from(itemView.context) .inflateTransition(R.transition.icon_expand_toggle) TransitionManager.beginDelayedTransition(parent, transition) isExpanded = !isExpanded setUpExpandedStatus()
I created animation and transitions samples including the one on the gif, you can check them out there.