-
Notifications
You must be signed in to change notification settings - Fork 173
MotionLayout in RecyclerView appears to not properly size cell when ViewHolder is bound for a recycled, animated cell #448
Description
Trying to use a MotionLayout to expand/collapse a particular view (change size and rearrange slightly) in a row in a RecyclerView list and seeing an issue with the cell height not properly being restored when I try to reset the MotionLayout transition when binding a new ViewHolder.
When a ViewHolder that was expanded is recycled the views that are part of the ViewHolder look like they've been correctly transitioned to the collapsed state, but the entire cell seems to stay the full expanded size.
Is there something obvious I'm missing? Using version: 2.1.2
A screen capture and minimal code sample follows...
Activity...
class MotionLayoutPlayground : AppCompatActivity() {
private lateinit var binding: ActivityMotionLayoutPlaygroundBinding
private val things = IntRange(0, 40).toList().map {
Thing(
name = "Thing $it",
description = "This is the description for thing $it"
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMotionLayoutPlaygroundBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = ThingAdapter(things)
}
}
Activity layout (activity_motion_layout_playground.xml) ...
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".motionplay.MotionLayoutPlayground">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Model...
data class Thing(
val name: String,
val description: String
)
Adapter...
class ThingAdapter(private val things: List<Thing>) : RecyclerView.Adapter<ThingAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = RowThingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding = binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(things.get(position))
}
override fun getItemCount() = things.size
class ViewHolder(val binding: RowThingBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(thing: Thing) {
// reset the MotionLayout to collapsed state
binding.rootMotionLayout.setTransition(R.id.start, R.id.end)
binding.rootMotionLayout.transitionToStart()
binding.nameTextView.text = thing.name
binding.descriptionTextView.text = thing.description
}
}
}
Row layout (row_thing.xml) ...
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout
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"
android:id="@+id/rootMotionLayout"
android:minHeight="168dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/light_blue"
app:layoutDescription="@xml/thing_cell_scene"
app:currentState="@id/start"
>
<TextView
android:id="@+id/nameTextView"
android:layout_width="150dp"
android:layout_height="150dp"
android:gravity="center"
android:padding="16dp"
android:background="@color/light_green"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="This is a name"
/>
<TextView
android:id="@+id/descriptionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/light_red"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/nameTextView"
tools:text="This is the description"
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
Motion scene (thing_cell_scene.xml) ...
<?xml version="1.0" encoding="utf-8"?>
<MotionScene
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<Transition
app:constraintSetStart="@id/start"
app:constraintSetEnd="@id/end"
app:duration="250"
>
<OnClick
app:clickAction="toggle"
app:targetId="@+id/nameTextView"
/>
</Transition>
<!-- Collapsed constraints -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/nameTextView"
android:layout_width="150dp"
android:layout_height="150dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
/>
<Constraint
android:id="@+id/descriptionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/light_red"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/nameTextView"
/>
</ConstraintSet>
<!-- Expanded constraints -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/nameTextView"
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<Constraint
android:id="@+id/descriptionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/light_red"
app:layout_constraintTop_toBottomOf="@id/nameTextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
</ConstraintSet>
</MotionScene>
