androidlistview

How to get a android ListView item selector to use state_checked


Tearing my hair out trying to get an Android ListView to do what I want.

I want to have a ListView in single choice mode with a custom row layout that has a different background color for selected, pressed and checked (i.e. the choice is shown by a color rather than a check mark - this is what I would normally call the "selection" but selection in android seems line I'm about to choose before I press it)

I thought of trying a background selector with the three states in it. It works fine for state_selected and state_pressed, but not state_checked. So I created a CheckableRelativeLayout that extends RelativeLayout and implements Checkable and used for the view of each row.

A simplified version is shown here:

<my.package.CheckableRelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:background="@drawable/bkg_selector">  
  >

     <ImageView android:id="@+id/animage"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_alignParentLeft="true"
     />
 </my.package.CheckableRelativeLayout>

bkg_selector looks like

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@drawable/purple" />
    <item android:state_checked="true" android:drawable="@drawable/red" />
    <item android:state_selected="true" android:drawable="@drawable/darkpurple" />
    <item android:drawable="@drawable/black" />
</selector>

The colors are defined elsewhere.

This still didn't work. So in the custom ListAdapter I tracked the "checked" row and tried (in getView)

if( position == checkedPosition ) ret.getBackground().setState(CHECKED_STATE_SET);

And it STILL doesn't work. How can I get it to do what I want?


Solution

  • You need to override onCreateDrawableState in your CheckableRelativeLayout and set Clickable="true" for it. My code for LinearLayout:

    public class CheckableLinearLayout extends LinearLayout implements Checkable {
    private boolean checked = false;
    
    public CheckableLinearLayout(Context context) {
        super(context, null);
    }
    
    public CheckableLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);       
    }
    
    private static final int[] CheckedStateSet = {
        R.attr.state_checked
    };
    
    public void setChecked(boolean b) {
        checked = b;
    }
    
    public boolean isChecked() {
        return checked;
    }
    
    public void toggle() {
        checked = !checked;
    }
    
    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CheckedStateSet);
        }
        return drawableState;
    }
    
    @Override
    public boolean performClick() {
        toggle();
        return super.performClick();
    }