Issue
I have a RecyclerView
which displays list items. These items expand when the user clicks them. The show animation works fine but every time an item gets clicked, the RecyclerView
seems to add an item at the very top position but then hides it so that it scrolls down as you can see in the GIF below.
My list item layout:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
app:layout_constraintBottom_toBottomOf="@+id/txv_item_history_bill_type"
app:layout_constraintTop_toBottomOf="@+id/txv_item_history_bill_type">
<ImageView
android:id="@+id/imv_item_history_category"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/txv_item_history_bill_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:fontFamily="sans-serif"
android:letterSpacing="0.15"
android:textAllCaps="true"
app:layout_constraintStart_toEndOf="@+id/imv_item_history_category"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txv_item_history_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:fontFamily="sans-serif"
android:letterSpacing="0"
android:textAllCaps="false"
android:textColor="@color/colorAccentTextColor"
android:textSize="24sp"
app:layout_constraintStart_toEndOf="@+id/imv_item_history_category"
app:layout_constraintTop_toBottomOf="@+id/txv_item_history_bill_type" />
<TextView
android:id="@+id/txv_item_history_date_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:fontFamily="sans-serif-medium"
android:letterSpacing="0.1"
android:textAllCaps="false"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="@+id/imv_item_history_category"
app:layout_constraintTop_toBottomOf="@+id/txv_item_history_description" />
</android.support.constraint.ConstraintLayout>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="72dp"
android:scrollbars="none">
<LinearLayout
android:id="@+id/ll_item_history_bill_action_buttons_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone">
<android.support.design.button.MaterialButton
android:id="@+id/btn_item_history_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/btn_edit"
style="@style/Widget.MaterialComponents.Button.OutlinedButton" />
<android.support.design.button.MaterialButton
android:id="@+id/btn_item_history_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/btn_delete" />
<android.support.design.button.MaterialButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/btn_edit_amount"
style="@style/Widget.MaterialComponents.Button.TextButton"/>
<android.support.design.button.MaterialButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/btn_edit_description"
style="@style/Widget.MaterialComponents.Button.TextButton"/>
<android.support.design.button.MaterialButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_change_category"
style="@style/Widget.MaterialComponents.Button.TextButton"/>
</LinearLayout>
</HorizontalScrollView>
<View
android:id="@+id/view_item_history_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:layout_marginStart="72dp"
android:background="@color/colorDivider" />
Code for expantion:
final boolean isExpanded = position == mExpandedPosition;
holder.mLlBillActionButtonsContainer.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(v -> {
mExpandedPosition = isExpanded ? -1 : position;
TransitionManager.beginDelayedTransition(recyclerView);
notifyDataSetChanged();
});
Solution
I believe this is happening due to the combination of these two lines in your code:
TransitionManager.beginDelayedTransition(recyclerView); notifyDataSetChanged();
RecyclerView supports animations by default, as long as you use the more-specific "notify" methods (like notifyItemChanged()
) instead of notifyDataSetChanged()
. So, if you can use those instead, you can delete the TransitionManager
call.
The good news is that you know which position was "expanded" before, so you can call notifyItemChanged()
on only the old position and the new position. This should get you much better animations.
Here's an updated listener for you:
holder.itemView.setOnClickListener(v -> {
int previousExpandedPosition = mExpandedPosition;
mExpandedPosition = isExpanded ? -1 : position;
if (previousExpandedPosition != -1) {
notifyItemChanged(previousExpandedPosition);
}
if (mExpandedPosition != -1) {
notifyItemChanged(mExpandedPosition);
}
});
Answered By - Ben P.