43

I'm using the RecyclerView like below:

<android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="320dp"
    android:layout_height="match_parent"/>

and my list item:

<LinearLayout  android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/selector_medium_high">
    <com.basf.suvinil.crie.ui.common.widget.CircleView
        android:id="@+id/circle"
        android:layout_width="22dp"
        android:layout_height="22dp"/>
    <TextView
        android:id="@+id/label"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="57.5dp"/>
</LinearLayout>

see in detail this part android:background="@drawable/selector_medium_high" it's a normal selector:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/background_high" android:state_activated="true"/>
    <item android:drawable="@color/background_high" android:state_pressed="true"/>
    <item android:drawable="@color/background_high" android:state_checked="true"/>
    <item android:drawable="@color/background_high" android:state_focused="true"/>
    <item android:drawable="@color/background_medium"/>
</selector>

but when I run this code, i have no changes in background color when I touch the row....

ademar111190
  • 14,215
  • 14
  • 85
  • 114
  • Unfortunately using focusable views is not a good way to track selection. Check my answer for a complete solution. – Greg Ennis Mar 03 '15 at 18:13

10 Answers10

55

Set clickable, focusable, focusableInTouchMode to true in all elements of RecyclerView "list".

Ivan Fazaniuk
  • 1,062
  • 3
  • 11
  • 26
Ahmed Ghonim
  • 4,455
  • 1
  • 17
  • 23
36

Add :

android:background="?android:attr/selectableItemBackground"

in item.xml

ʍѳђઽ૯ท
  • 16,646
  • 7
  • 53
  • 108
100rbh
  • 763
  • 1
  • 7
  • 10
18

If nothing of this works for you, like it didn't for me, use this code:

android:foreground="?android:attr/selectableItemBackground"

The trick is in android:foreground attribute...

NixSam
  • 615
  • 6
  • 20
  • This would have the adverse effect of overlaying the actual content and can only be used in `FrameLayout` before API 23 (https://stackoverflow.com/a/52411921/461982) – Abandoned Cart Jun 07 '19 at 13:13
14

Adding android:background="?android:attr/selectableItemBackground" to my_list_item.xml's root layout seems to work for me (assuming you want the default selection colour).

Also make sure the root layout's android:layout_width is match_parent rather than wrap_content to ensure that the whole row is selectable.

Lawrr
  • 400
  • 5
  • 5
  • How can you change the default selection color when using ```android:background="?android:attr/selectableItemBackground"```? – JMB Apr 22 '20 at 03:52
7

Unfortunately using focusable views to simulate item selection is not a good solution because:

  • Focus is lost when notifyDataSetChanged is called
  • Focus is problematic when child views are focusable

I wrote a base adapter class to automatically handle item selection with a RecyclerView. Just derive your adapter from it and use drawable state lists with state_selected, like you would do with a list view.

I have a Blog Post Here about it, but here is the code:

public abstract class TrackSelectionAdapter<VH extends TrackSelectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
    // Start with first item selected
    private int focusedItem = 0;

    @Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        // Handle key up and key down and attempt to move selection
        recyclerView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();

                // Return false if scrolled to the bounds and allow focus to move off the list
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                        return tryMoveSelection(lm, 1);
                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                        return tryMoveSelection(lm, -1);
                    }
                }

                return false;
            }
        });
    }

    private boolean tryMoveSelection(RecyclerView.LayoutManager lm, int direction) {
        int tryFocusItem = focusedItem + direction;

        // If still within valid bounds, move the selection, notify to redraw, and scroll
        if (tryFocusItem >= 0 && tryFocusItem < getItemCount()) {
            notifyItemChanged(focusedItem);
            focusedItem = tryFocusItem;
            notifyItemChanged(focusedItem);
            lm.scrollToPosition(focusedItem);
            return true;
        }

        return false;
    }

    @Override
    public void onBindViewHolder(VH viewHolder, int i) {
        // Set selected state; use a state list drawable to style the view
        viewHolder.itemView.setSelected(focusedItem == i);
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View itemView) {
            super(itemView);

            // Handle item click and set the selection
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Redraw the old selection and the new
                    notifyItemChanged(focusedItem);
                    focusedItem = mRecyclerView.getChildPosition(v);
                    notifyItemChanged(focusedItem);
                }
            });
        }
    }
} 
Greg Ennis
  • 14,917
  • 2
  • 69
  • 74
5
    viewHolder.mRlPrince.setOnTouchListener(new View.OnTouchListener() {
        @Override public boolean onTouch(View v, MotionEvent event) {

            if (event.getAction()==MotionEvent.ACTION_DOWN){
                viewHolder.mRlPrince.setBackgroundColor(Color.parseColor("#f8f8f8"));
            }if (event.getAction()==MotionEvent.ACTION_UP){
                viewHolder.mRlPrince.setBackgroundColor(Color.WHITE);
            }

            return false;
        }
    });
jingyuan iu
  • 93
  • 1
  • 1
4

Add this below attribute in your Item "my_list_item.xml"
android:descendantFocusability="blocksDescendants" This works for me !

Rajiv Manivannan
  • 504
  • 7
  • 15
2

You need to set android:clickable="true" in the element xml, and if you have more selectors in some view inside your view, you need to set android:duplicateParentState="true" there too. Thats works on pre honeycomb apis.

Bruno Pinto
  • 2,013
  • 3
  • 23
  • 33
1

As many others answered the only way is to combine selectors and new Classes to keep track of the selection, but better to delegate this calculation to the Adapter. The library FlexibleAdapter keeps track of the selections for you, configuration change is also compatible.

Backgrounds color with ripple can now be done without XML part, but in the code to manage dynamic data.

Finally, you can use lot of features with the same library, selection is a tiny basic feature you can have.

Please have a look at the description, Wiki pages and full working example: https://github.com/davideas/FlexibleAdapter

Davideas
  • 3,226
  • 2
  • 33
  • 51
1

on your design or xml file just place the following lines.

android:clickable="true"
android:focusable="true"
android:background="?android:attr/activatedBackgroundIndicator"
Juboraj Sarker
  • 947
  • 1
  • 9
  • 13