0

I'm implementing a CAB, and my ListView is populated from a database. When I scroll the ListView or rotate the device screen, the background of the previously selected items is reverted to the default background.

A holder which I use to store the selection status to restore it in bindView:

private static class ViewInfo {
    boolean selected;
}

bindView:

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        view.setOnLongClickListener(mOnLongClickListener);
        Object tag = view.getTag();
        if (tag != null) {
            ViewInfo info = (ViewInfo) view.getTag();
            view.setSelected(info.selected);
        } else {
            view.setTag(new ViewInfo());
        }

        // Load data from the database here
    }

OnLongClickListener:

mOnLongClickListener = new OnLongClickListener() {

    @Override
    public boolean onLongClick(View v) {
        ViewInfo viewInfo = (ViewInfo) v.getTag();
        v.setSelected(viewInfo.selected = !viewInfo.selected);
        return true;
    }
};

My ListView:

<ListView
    android:id="@+id/filtering_list"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:choiceMode="multipleChoiceModal"
    android:drawSelectorOnTop="true" />

My list item background filtering_list_item_bg:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@android:color/holo_blue_light" android:state_pressed="true"/>
    <!-- pressed -->
    <item android:drawable="@android:color/holo_blue_light" android:state_focused="true"/>
    <!-- focused -->
    <item android:drawable="@android:color/background_light" android:state_selected="true"/>

</selector>

My list item layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/filtering_list_item_bg"
    android:paddingBottom="12dp"
    android:paddingLeft="8dp"
    android:paddingRight="8dp"
    android:paddingTop="12dp" >

    <!-- text views, image views, etc. -->

</RelativeLayout>

What I can't understand here is why setSelected is called in bindView but doesn't change the background.

Maksim Dmitriev
  • 5,985
  • 12
  • 73
  • 138

2 Answers2

1

Where are you setting the selector to the List Item? You need to do this.

Try this - go to your List Item XML code, let assume the outer layout looks something like the following

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="@drawable/my_selector"
android:layout_width="match_parent"
android:layout_height="match_parent">

Note the line - android:background="@drawable/my_selector" - you need add this in your List Item, then when selected it will change color etc.

Add the Long click listener to your listView and not item - line 173 in your bitbucket repo should read

 mAdapter = new ListAdapter(getActivity(),
                R.layout.filter_list_item,
                null,
                COLUMNS,
                new int[] { R.id.ip_address, R.id.port },
                0);

        mFilteringListView.setAdapter(mAdapter);

       mFilteringListView.setOnLongClickListener(new AdapterView.OnItemLongClickListener() {
        @Override
        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
            ViewInfo viewInfo = (ViewInfo) v.getTag();
                if (viewInfo.selected) {
                    mSelectedCount--;
                } else {
                    mSelectedCount++;
                }
                v.setSelected(viewInfo.selected = !viewInfo.selected);
                if (mSelectedCount > 0) {
                    mActivity.startActionMode(mActionModeCallback);
                }
                return true;
        }
    });
Zain
  • 2,336
  • 1
  • 16
  • 25
  • I edited my question. Do I set the background correctly? What I added doesn't work. – Maksim Dmitriev May 21 '15 at 14:26
  • Remote these two lines and it should work - android:choiceMode="multipleChoiceModal" android:drawSelectorOnTop="true" – Zain May 21 '15 at 14:28
  • Yes I meant *remove. What does the rest of the List Item layout look like? Can you actually see any items in your List? – Zain May 21 '15 at 14:37
  • yes, I see all my items. The problem is that `setSelected` is called in `bindView` but doesn't change the background. – Maksim Dmitriev May 21 '15 at 14:39
  • The code is available on https://bitbucket.org/MaksimDmitriev/norootfirewall/src/436224e47847363f3733df4a04cda40ff1d496c7/src/com/norootfw/gui/FilteringListActivity.java?at=issue_ui_screenshots – Maksim Dmitriev May 21 '15 at 14:40
  • OK - so it seems as though the OnLongClick is not firing? Try adding LongClick listener to list and not item - I've modified code above in the answer – so when you set the adapter, you set the Long Click listener – Zain May 21 '15 at 15:31
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/78450/discussion-between-maksim-dmitriev-and-zain). – Maksim Dmitriev May 21 '15 at 15:52
1

I managed to save the color of the selected items without setSelected.

My list item doesn't have a background color. It's assigned programmatically depending on the current state (selected/not selected).

My list item layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="12dp"
    android:paddingLeft="8dp"
    android:paddingRight="8dp"
    android:paddingTop="12dp" >
    <!-- text views, image views, etc. -->
</RelativeLayout>

My ListView.

<ListView
        android:id="@+id/filtering_list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

OtItemLongClickListener. I save the selected items to a hash table because it offers O(1) for the get, put, and remove operations.

mFilteringListView.setOnItemLongClickListener(new OnItemLongClickListener() {

    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        if (mAdapter.mSelectedIds.get(id) == null || !mAdapter.mSelectedIds.get(id)) {
            mAdapter.mSelectedIds.put(id, true);
            view.setBackgroundColor(getActivity().getResources().getColor(R.color.highlighted_item));
        } else {
            /*
             * I don't put a false because there is no reason to store unselected items.
             * If the user selects and unselects items much, the hash table will grow
             * rapidly
             * 
             * Maksim Dmitriev
             * May 21, 2015
             */
            mAdapter.mSelectedIds.remove(id);
            view.setBackgroundColor(getActivity().getResources().getColor(android.R.color.white));
        }
        return true;
    }
});

The ListView adapter where the background is restored for every item from the hash table:

private static class ListAdapter extends SimpleCursorAdapter {

    final Context mContext;
    LongSparseArray<Boolean> mSelectedIds = new LongSparseArray<Boolean>();

    public ListAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
        super(context, layout, c, from, to, flags);
        mContext = context;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        long id = cursor.getInt(cursor.getColumnIndex(Columns._ID));
        if (mSelectedIds.get(id) == null || !mSelectedIds.get(id)) {
            view.setBackgroundColor(mContext.getResources().getColor(android.R.color.white));
        } else {
            view.setBackgroundColor(mContext.getResources().getColor(R.color.highlighted_item));
        }
        // fill the other text views, image views, etc.
    }
}
Maksim Dmitriev
  • 5,985
  • 12
  • 73
  • 138