Listview selector with colored background and ripple effect

Standard ListView selector in android L developer preview uses colorControlHighlight for the ripple effect on touch and has a transparent background in unfocused state.

I would like to define a ListView item that has a colored background and still shows the ripple effect on touch with the same highlight color. Now, if I define the following drawable:

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="?android:colorControlHighlight">
    <item android:drawable="@color/my_background_color"/>
</ripple>

it works, but the ripple starts in the middle of the ListView item, regardless of the touch position. If I use the same background outside of the ListView, e.g. for a LinearLayout, it works like expected (the ripple starts on the touch position).


I've managed to get individually colored list items while maintaining the ripple effect. Set the background of your list items using whatever adapter you have and set the listview to show the selector on top:

<ListView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:drawSelectorOnTop="true" />

This will draw the ripple effect above the background.


As far as I can tell this bug is only in Android 5.0, not 5.1. The trick seems to be to use Drawable#setHotspot as a Google dev hints to here https://twitter.com/crafty/status/561768446149410816 (because obscure twitter hints are a great form of documentation!)

Assume you have a row layout something like this

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

      <LinearLayout
            android:id="@+id/row_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:background="?attr/selectableItemBackground">

          .... content here .....

     </LinearLayout>

</FrameLayout>

The following worked for me

            row.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    v.findViewById(R.id.row_content)
                        .getBackground()
                        .setHotspot(event.getX(), event.getY());

                    return(false);
                }
            });

I've found that it only seems to work correctly if you apply the background to the root element of the list item.

Also, consider using the new RecyclerView instead of a ListView

List item view example:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/list_padding"
    android:layout_marginLeft="@dimen/list_padding"
    android:layout_marginRight="@dimen/list_padding"
    android:padding="@dimen/list_padding"
    android:background="@drawable/ripple_bg">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Large Text"
        android:id="@+id/tvTitle"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Small Text"
        android:id="@+id/tvSubtitle" />

</RelativeLayout>

i adapted @ArhatBaid 's answer a littlebit, tested it and it works perfectly:

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="?android:colorControlHighlight">
    <item android:drawable="@color/light_grey_header_navigation_drawer"/>
</ripple>

So, this allows you to set a background color and still have the ripple effect.
for me target and minSdk are 21.