15

You can not use the following state drawable as background for listview items.

<item android:drawable="@drawable/ic_launcher" android:state_activated="true"/>

On Pre Honeycomb Devices because this selector is not supported there and the android version does not keep track of the activated item(s).

How can this behaviour be emulated? Especially when using fragements (list on the left in one fragment and depending on what is selected a detail view on the right) this indicator is very important.

I know that this question was asked before here but the accepted answer there links to a blog article which states in "Step 4" that there is no possibility to have the activated indicator and instead only disables the use to prevent errors. This leads to the fact that no indicator is displayed which is what I'm searching for.

Community
  • 1
  • 1
theomega
  • 31,591
  • 21
  • 89
  • 127

4 Answers4

28

I solved the problem using a small trick: By missusing the state_checked property, which exists since Android Version 1, it is possible to emulate the state_activated behaviour. There is no need to modify the List adapter or save the state yourself.

I wrote a detailed example containing all code necessary to repoduce and published it on a github repository.

theomega
  • 31,591
  • 21
  • 89
  • 127
  • If only I had seen this 6 hours ago.. :-( Thanks a lot, especially for the well-documented github repo! – Gautam Jun 08 '13 at 01:11
  • 2
    This solution will not work if you need a checkbox in your listview - then it will be checked with any selection – Lonli-Lokli Apr 07 '14 at 12:37
  • @Lonli-Lokli That is true, my solution is only a workaround or a trick, it has its problems.... But good to mention it. – theomega Apr 07 '14 at 14:53
  • I removed propogating to childs and set android:descendantFocusability="blocksDescendants" on item layout. Now I have only problems with scrolling of selected TextView (it stopped). So thanks for the solution! As for me it'better than any java-modifications for adapters. – Lonli-Lokli Apr 07 '14 at 20:07
3

Much Like Chris Jenkins suggested, I just add a little code to my getView override:

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    // ...

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        convertView.setBackgroundColor(Color.WHITE);
        if (listView.isItemChecked(position)) {
            convertView.setBackgroundColor(Color.LTGRAY);
        }
    }
}

The item being checked or not was automatically handled in my case (using CHOICE_MODE_MULTIPLE and v7 ActionMode.Callback with setOnItemLongClickListener calling the callback)

Adam Johns
  • 35,397
  • 25
  • 123
  • 176
0

Just change the background of the selected list item in your adapter to a 'activated' drawable.

So in my adapter get view method might look a little like this:

getView(int pos, View convertView, ViewGroup parent){
//... etc get view etc
    if(mSelectedItemPos == pos){
        v.setBackgroundDrawable(ctx.getResources().getDrawable(R.drawable.list_item_selected_state);
        //or v.setBackgroundResource(R.drawable.list_item.....);
    } else {
        v.setBackgroundDrawable(ctx.getResources().getDrawable(R.drawable.list_item_selector);            
    }
}

Thats all i do if i want to support selected/activated states on views. Little more work but can be as flexible as you want.

Chris.Jenkins
  • 13,051
  • 4
  • 60
  • 61
0

I've done it manually in the following manner before:

Create an array to hold the selected state of the list, initialize it in the adapter constructor, then reference that in your getView method (in case the currently selected item scrolls out of view) and your onItemClick method (to change the current selection and turn off the old one).

public static boolean selectedStatus[];  // array to hold selected state
public static View oldView;        // view to hold so we can set background back to normal after 

Constructor initialize the array

public class MyCursorAdapter extends SimpleCursorAdapter {
    private LayoutInflater mInflater;
    private int layout;

    public MyCursorAdapter(Context context, int layout, Cursor c,
        String[] from, int[] to, int flags) {
            super(context, layout, c, from, to, flags);
            mInflater = LayoutInflater.from(context);
            this.layout = layout;
            selectedStatus = new boolean[c.getCount()];
            for (int i = 0; i < c.getCount(); i++) {
            selectedStatus[i] = false;  // Start with all items unselected
        }
    }
} 

getView needed for when the child scrolls out of view

@Override 
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null)
         convertView = View.inflate(context, layout, null);

    if(selectedStatus[position] == true){
        v.setBackgroundResource(R.color.blue);                          
    } else {
        v.setBackgroundResource(R.color.black);
    }
    return v; 
} 

onItemClick change selected item in array and on screen

lv.setOnItemClickListener(new OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view,
        int position, long id) {           

        mRowId = id;
        EventDisplayFragment eventdisplay = new EventDisplayFragment();
        getFragmentManager().beginTransaction()
                .replace(R.id.rightpane, eventdisplay).commit();
        if(MyCursorAdapter.oldView != null){
            MyCursorAdapter.oldView.setBackgroundResource(R.color.black);   // change the background of the old selected item back to default black                 }
        MyCursorAdapter.oldView = v;                                        // set oldView to current view so we have a reference to change back on next selection
        for (int i = 0; i < selectedStatus.length; i++) {
            if(i == position){                              // set the current view to true and all others to false
                    selectedStatus[i] = true;
                } else {
                    selectedStatus[i] = false;
                }
            }
        }
        v.setBackgroundResource(R.color.blue);
        return true;
    }
});
Barak
  • 16,318
  • 9
  • 52
  • 84
  • That seems a solution, I though of extending the ListView directly because it would provide a more reusable solution. Thanks anyway. – theomega Nov 23 '12 at 15:50