In this case the question is pertaining to a RecyclerView, where I'd like the ripple effect to animate (roughly if not exactly) from where the finger touched with a custom colour to the full extent of a view, much like a listview row in terms of the view's layout, and finally end its animation with the row selected in another colour.
I have
items_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rowLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="5dp"
android:paddingTop="5dp"
android:background="@drawable/items_ripple_state_selector"
>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center">
<TextView
android:id="@+id/item_unit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/item_amount"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="unit" />
<TextView
android:id="@+id/item_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="amount" />
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="4.6"
android:gravity="center_vertical">
<TextView
android:id="@+id/item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="title"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/item_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/item_title"
android:layout_alignWithParentIfMissing="true"
android:layout_below="@+id/item_title"
android:text="description" />
</RelativeLayout>
</LinearLayout>
and
items_ripple_state_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<selector>
<item android:state_pressed="true" android:drawable="@color/state_activated" />
<item android:state_selected="true" android:drawable="@color/state_selected" />
<item android:drawable="@android:color/transparent" />
</selector>
</item>
<item>
<ripple android:color="@color/primary">
<item android:id="@android:id/mask">
<color android:color="@android:color/white" />
</item>
</ripple>
</item>
</layer-list>
Tests that came out bad
I have done tests on the following devices, in which the ripple effect animation stops way too early, resulting more in a "blip"/"ping" rather than a full expansion to the view's edges.
Tests that came out good
I have done tests on the following devices, in which the ripple effect animation animates beautifully in what I would expect is its full length, to the view's edges:
I can't for the life of me figure out what's going on with this. It looks pretty horrible on the N6 and N5X as the animation stops mid-way, with no distinct pattern as to when the animation stops.
I've read quite a few SO questions as well as RecyclerView, StateListDrawable and RippleDrawable documentation, but there's just nothing that gives any explanation to the behaviour I see.
The closest I have come, which is what I'm currently using, and what I've shared in this post in terms of code, came from this answer https://stackoverflow.com/a/31335539/975641
Does anyone have an idea as to why this happens and how to fix it?
I don't know the specific reason for why it works in Marshmallow, but not Nougat. However, most commonly when you see this issue, it's because the RecyclerView
is refreshing and/or re-binding way too much.
Basically what would happen is
Most commonly this occurs when calling either RecyclerAdapter#notifyItemChanged(int)
method or RecyclerAdapter#notifyDatasetChanged()
too many times.
It would be best to use the RecyclerView.Adapter#notifyItem____
methods if possible. notifyDatasetChanged()
will cause a rebind on all the items in the list. Using the notifyItem_____
methods will make sure that only individual items are updated when they need to be. Otherwise, you need to ensure to only call nofityDatasetChanged()
when there are actual changed in the dataset.
Another common issue would be if the app is calling invalidate
in either the RecyclerView
or any parent of the RecyclerView
. This would cause a refresh of the entire View tree which would require a rebind for all the items in a RecyclerView
.