Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setSelected() works buggy with ListView

I'm trying just to make clicked items of ListView to change background. But it seems to me not actually possible. there are lots of posts with examples of doing that but none of them works reliably. As I understood - it's somehow related to "recycling".

I call view.setSelected() in adapter's OnItemClickListener and it nicely applies another background to selected item according to my settings. But when I select the item which causes ListView to lack space (not important how exactly) and a scollbar appears (or disappears) inside of ListView - android forgets my selection and the default style is applied. Same bug occurs when rotating screen - the item deselects. So I think that "deselection" occurs while adapter's getView() is being called.

It's interesting that my onClick event causes sending a json request to a background service and recieving and decoding a json response, so it takes some time between an item click and activity content change. This is how it looks:

  1. I click a ListView item. It changes background to "selected color".
  2. Few moments I'm waiting.
  3. The activity content is changing according to a service response. A scrollbar appears inside of ListView. Item background changes to "default color" (item is deselected).

Clicking on items which don't cause a scrollbar to appear works nicely - selected items do not become deselected after processing service's response.

Trying to call setSelected() inside of adapter's getView() gives no affect on the bug. Item still being deselected. I tried to set item's background manually in getView() - and it became more interesting: items which cause appearance of a scrollbar started to work properly, but items which don't cause apperance of scrollbar (actually it means that they don't cause getView() calling) stopped working!

All code is quite complex, so I'll post just some important fragments. Here's my OnItemClickListener:

private AdapterView.OnItemClickListener onCategoryClickListener =
        new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, final View view, final int position,
            long id) {
        categoriesAdapter.setSelectedPosition(position);
        view.setSelected(true);
        // More code here
    }
};

and here's a fragment of my Adapter's code:

private int selectedPosition;
private boolean selectable = true;

public void setSelectedPosition(int position) {
    this.selectedPosition = position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    TextView label = (TextView) View.inflate(context, textViewResourceId, null);
    label.setText(getName(values.get(position)));
    if(selectable) {
        label.setBackgroundResource(R.drawable.list_selector);
        if(position == selectedPosition) {
            label.setSelected(true);  // This does not work. Why?
            label.setBackgroundColor(  // This gives strange results
                context.getResources().getColor(R.color.list_item_selected_color));
        } else {
            // Similar code here, but for deselecting items.
        }
    }
    return label;
}

and here is my selector:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@color/list_item_default_color"
        android:state_selected="false" />
    <item
        android:drawable="@color/list_item_selected_color"
        android:state_selected="true"/>
</selector>

I searched a lot for how to make it work but nothing helps. Here are some things that I tried:

  • Running view.setSelected() inside of view.post()
  • Running list.setSelection() - what is this method for? it does nothing!
  • Initializing TextView more accurate, checking if convertView is null. Gives bad, very bad results - causes ListView items to shuffle (without any affect on their selection status).
  • Did not use ViewHolder because I have no complex layouts for Item, I just have a simple TextView.
like image 411
Alexander Konotop Avatar asked Dec 26 '22 01:12

Alexander Konotop


1 Answers

For API 11+:

1) Set list view to single choice:

<ListView
        android:choiceMode="singleChoice" />

2) Set the background of the root element of your item layout to your selector:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@drawable/selector">

3) Change android:state_selected to android:state_activated:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/list_item_selected_color"
        android:state_activated="true"/>
    <item android:drawable="@color/list_item_default_color"/>
</selector>

4) Highlight item using listView.setItemChecked(index, true);

Note: To make it clear regarding view's states, specially state_activated you can check this post; it is interesting.

like image 82
tachyonflux Avatar answered Jan 07 '23 04:01

tachyonflux