从适配器中设置ListView项目的选中状态

17

我有一个ListView,其中显示两种项目。其中一种包含CheckedTextView。作为适配器,我正在使用扩展ArrayAdapter的自定义适配器,并包含有关所选/未选状态信息的数据结构。

我的数据结构中有些项目被标记为已选择,所以当创建ListView时,我希望复选框被标记。我尝试在适配器的getView()方法中使用CheckedTextView的setChecked()方法来做到这一点,但它不起作用。我找到了信息,应该在ListView级别上使用setItemChecked()来完成,它确实有效,但我认为它没有意义,因为要使其工作,我必须在activity的onCreate()中循环调用setItemChecked()以针对那些已选中的所有项目。项目列表可能非常长,只有其中几个被选中,所以这是浪费。

为什么在getView()中调用setChecked()不起作用?有更好的方法吗(我是Android新手)?

下面是我的已选项布局和适配器。

布局:

<?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"
/>

适配器:

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];
            }
        };
}
}

在Activity的onCreate()方法中

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

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

编辑:

在找到更好的解决方案之前,我在 Activity 中实现了检查功能:

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

我原以为性能问题是唯一的潜在问题,但我发现了另一个更严重的问题。列表启用了文本过滤器(mListView.setTextFilterEnabled(true))。以下是故障情况:

  1. 我打开一个新列表
  2. 我勾选第二个项目
  3. 我开始输入来过滤一些项
  4. 当过滤完成时,第二个项目仍然被勾选,尽管它不是我勾选的那个项目

我认为 setItemChecked() 标记了列表上的固定位置而非标记这个位置上的项目 - 即便列表位置已经改变,也需要保持项目状态。

虽然性能问题在一定程度上可以被忽略,但这个新问题确实让我无法继续,所以我真的很感谢任何关于它的帮助。

2个回答

29

如果你使用ListView.CHOICE_MODE_MULTIPLECHOICE_MODE_SINGLE,则在getView被调用后,列表项的选中状态将被ListView覆盖。您需要使用以下结构:

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

另一种可能性是使用ListView.CHOICE_MODE_NONE,并自己管理检查状态。


我也遇到了同样的问题。当滚动ListView时,我的选中项会消失。我已经尝试将你的代码块放在我的适配器中,但是当我滚动ListView时,我的选中项仍然会消失。 - salih
谢谢,我花了很长时间才找到解决方案。 - 0x6C38

-1

请查看以下步骤: 首先,在项目 XML 中进行更改, 在复选框中添加单行。

`android:onClick="chatWithFriend"` 

所以当点击复选框时,在活动名称中创建一个方法

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

现在在getView方法中,在返回convert view之前添加这行代码

holder.checkbox.setTag(position);

现在您将获得复选框的位置

我希望这能帮到您。


也许我没有表达清楚。获取项目位置方面我没有任何问题。所有的“点击”操作都可以正常工作。不起作用的是在新创建的列表视图上使所选项目(此信息包含在传递给适配器的数据中)被选中。 - Izydorr
这可能会有所帮助:http://about-android.blogspot.com/2010/04/steps-to-implement-expandablelistview.html - Sameer Z.
抱歉,但我不认为这对我有帮助 :( - Izydorr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,