设置从适配器检查的 ListView 项目
我有一个 ListView 显示 2 种项目。其中一种包含 CheckedTextView。作为适配器,我使用自定义适配器扩展 ArrayAdapter,其数据结构包含有关内部已检查/未检查状态的信息。
有些项目被标记为选中(在我的数据结构中),所以我当然希望在创建 ListView 时标记复选框。我尝试使用 CheckedTextView 的 setChecked() 方法在适配器中的 getView() 方法上执行此操作,但它不起作用。我发现信息表明它应该使用 setItemChecked() 在 ListView 级别上完成,它确实有效,但对我来说没有意义,因为要使其工作,我必须循环 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];
}
};
}
}
以及一个活动的 onCreate()
...
mAdapter = new MultiListAdapter(this, R.id.head, mItems);
setListAdapter(mAdapter);
final ListView listView = getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setItemsCanFocus(false);
...
编辑:
在找到更好的解决方案之前,我在活动中实现了检查:
...
int len = mListView.getCount();
for (int i = 0; i < len; i++) {
if (((MultiChoiceItem) mListView.getItemAtPosition(i)).selected) {
mListView.setItemChecked(i, true);
}
}
...
我认为性能将是唯一的潜在问题,但我发现了另一个问题 - 更严重。该列表启用了文本过滤器(mListView.setTextFilterEnabled(true))。这是失败的场景:
- 我正在打开新列表,
- 我正在检查第二项,
- 当过滤完成后,我开始输入以过滤某些项目
- ,第二项仍然被检查,即使它不是我检查的那个
我假设的setItemChecked() 标记列表上已检查的固定位置,而我需要一些东西来标记该位置上的项目 - 即使列表上的位置发生变化也保持状态。
虽然性能问题可能(在某种程度上)被忽略 - 这个新问题确实阻碍了我,所以我非常感谢任何帮助。
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:
- I'm opening new list
- Im checking let's say 2-nd item
- I start typing to filter some items
- 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您使用
ListView.CHOICE_MODE_MULTIPLE
或CHOICE_MODE_SINGLE
,则在调用getView
后,项目的检查状态将被 ListView 覆盖。您必须使用以下构造:另一种可能性是使用 ListView.CHOICE_MODE_NONE 并自行管理检查状态。
If you use
ListView.CHOICE_MODE_MULTIPLE
orCHOICE_MODE_SINGLE
the check states of the items will be overridden by ListView aftergetView
is being called. You have to use the following construct:Another possiblity is to use
ListView.CHOICE_MODE_NONE
and manage the check states by yourself.查看步骤
首先在 item xml 中进行更改
在复选框中添加单行,
以便当单击复选框时
然后在活动名称中创建一个方法
之前在 getView 中添加这一行
现在在 return Convert viewholder.checkbox.setTag(position);
;现在您将获得复选框的位置,
希望这对您有所帮助。
Check out the steps
First in item xml make the changes
add single line in check box
so when the click on cheackbox
then create the one method in activity name
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.