Setting ListView item checked from Adapter
Asked Answered
A

2

17

I have a ListView which displays 2 kinds of items. One of these kinds contains CheckedTextView. As an adapter I'm using custom adapter extending ArrayAdapter with data structure containing information about checked/unchecked states inside.

Some of the items are marked as selected (in my data structure) so of course I'd like the checkboxes to be marked when the ListView is created. I tried to do it on getView() method in adapter with CheckedTextView's setChecked() method but it doesn't work. I found information that it should be done on a ListView level with setItemChecked() and it does work but it doesn't make sense to me because to make it work I would have to loop all items in activity's onCreate() calling setItemChecked() for those selected. The list of items may be very long with only a few of them selected so it's a waste.

Why calling setChecked() in getView() isn't working? Is there a better way of doing this (I'm an Android newbie).

Below you have my checked item layout and the adapter.

Layout:

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:textAppearance="@style/listViewItemText"
    android:checkMark="?android:attr/listChoiceIndicatorMultiple"
    android:paddingLeft="6dp"
    android:paddingRight="6dp"
/>

Adapter:

package com.melog.re.droid.search;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import android.widget.TextView;

import com.melog.re.droid.core.R;
import com.melog.re.droid.search.MultiCriterionValue.MultiCriterionSingleValue;
import com.melog.re.droid.search.MultiListAdapter.MultiChoiceItem;

public class MultiListAdapter extends ArrayAdapter<MultiChoiceItem> {
    private static final int VIEW_TYPES_COUNT = 2;

    public static final int VIEW_TYPE_GROUP = 0;
    public static final int VIEW_TYPE_ITEM = 1;

    private LayoutInflater mInflater;

    public MultiListAdapter(Context context, int textViewResourceId, List<MultiChoiceItem> objects) {
        super(context, textViewResourceId, objects);
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getItemViewType(int position) {
        MultiChoiceItem item = getItem(position);
        return (item.children.size() == 0) ? VIEW_TYPE_ITEM : VIEW_TYPE_GROUP;
    }

    @Override
    public int getViewTypeCount() {
        return VIEW_TYPES_COUNT;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO: optymalizacja ViewHolder
        MultiChoiceItem item = getItem(position);
        int viewType = getItemViewType(position);
        switch (viewType) {
        case VIEW_TYPE_GROUP:
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.multi_choice_group_item, parent, false);
            }
            TextView groupLabel = (TextView) convertView.findViewById(android.R.id.text1);
            groupLabel.setText(item.toString());
            break;
        case VIEW_TYPE_ITEM:
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.multi_choice_child_item, parent, false);
            }
            CheckedTextView itemLabel = (CheckedTextView) convertView.findViewById(android.R.id.text1);
            itemLabel.setText(item.toString());
            itemLabel.setChecked(item.selected);
            break;
        }

        return convertView;
    }

    public static class MultiChoiceItem implements Parcelable {
        public static final int CONTENTS_DESCR = 1;

        public String label;
        public String value;
        public boolean selected = false;
        public ArrayList<MultiChoiceItem> children = new ArrayList<MultiChoiceItem>();

        public MultiChoiceItem(String l, String v, boolean sel) {
            label = l;
            value = v;
            selected = sel;
        }
        public MultiChoiceItem(MultiCriterionSingleValue v) {
            label = v.toString();
            value = v.value;
        }
        public MultiChoiceItem(Parcel in) {
            label = in.readString();
            value = in.readString();
            selected = (in.readInt() == 1);
        }
        @Override
        public String toString() {
            return label;
        }
        @Override
        public int describeContents() {
            return CONTENTS_DESCR;
        }
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(label);
            dest.writeString(value);
            dest.writeInt(selected ? 1 : 0);
        }

        public static final Parcelable.Creator<MultiChoiceItem> CREATOR = new Parcelable.Creator<MultiChoiceItem>() {
            public MultiChoiceItem createFromParcel(Parcel in) {
                return new MultiChoiceItem(in);
            }

            public MultiChoiceItem[] newArray(int size) {
                return new MultiChoiceItem[size];
            }
        };
}
}

And a piece of activity's onCreate()

...     
mAdapter = new MultiListAdapter(this, R.id.head, mItems);
setListAdapter(mAdapter);

final ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setItemsCanFocus(false);
...

Edit:

Until finding a better solution I implemented the checking in the Activity:

...
int len = mListView.getCount();
for (int i = 0; i < len; i++) {
    if (((MultiChoiceItem) mListView.getItemAtPosition(i)).selected) {
        mListView.setItemChecked(i, true);
    }
}
...

I thought that performance will be the only potential problem but I found another one - much more serious. The list has text filter enabled (mListView.setTextFilterEnabled(true)). Here's the failure scenario:

  1. I'm opening new list
  2. Im checking let's say 2-nd item
  3. I start typing to filter some items
  4. when the filtering is done, the 2-nd item is still checked even though it's not the one I checked

I assume that setItemChecked() marks a fixed position on the list checked while I need something that will mark the Item held on ths position - keeping the state even when the position on the list changes.

While performance issue could be (to some point) ignored - this new problem is really blocking me, so I would really appreciate any help with it.

Anabantid answered 26/8, 2011 at 9:21 Comment(0)
P
29

If you use ListView.CHOICE_MODE_MULTIPLE or CHOICE_MODE_SINGLE the check states of the items will be overridden by ListView after getView is being called. You have to use the following construct:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
   ((ListView)parent).setItemChecked(position, true);
}

Another possiblity is to use ListView.CHOICE_MODE_NONE and manage the check states by yourself.

Penicillium answered 14/3, 2013 at 17:43 Comment(2)
I'm facing with same problem. My checked items are disappear when scrolling the listview. I've tried your code blok inside my adapter but still my checked items are disappearing when scrolling the listview .Dorotea
Thanks, I had to look for quite some time to find a solution.Rhett
I
-1

Check out the steps First in item xml make the changes add single line in check box

`android:onClick="chatWithFriend"` 

so when the click on cheackbox then create the one method in activity name

  public void chatWithFriend(View view){}
here you will get the view of check box,

Now add the line in getView before return convert view

holder.checkbox.setTag(position);

Now u will get the position of the check box

I hope this will help you.

Iatrics answered 26/8, 2011 at 11:29 Comment(3)
Maybe I didn't make myself clear. I don't have any problem with getting the item's position. All "clicking" actions work just fine. What isn't working is making the selected items (this information is in data passed to the adapter) checked on newly created list view.Anabantid
this may help about-android.blogspot.com/2010/04/…Iatrics
I'm sorry but I don't see how this could be helpful for me :(Anabantid

© 2022 - 2024 — McMap. All rights reserved.