过滤Listview数据后勾选(选中)复选框的位置发生变化
Posted
技术标签:
【中文标题】过滤Listview数据后勾选(选中)复选框的位置发生变化【英文标题】:postition of ticked (checked) checkbox changes after filtering data of Listview 【发布时间】:2012-12-09 16:46:03 【问题描述】:在我根据EditText
中的文本过滤输入并检查CheckBox
以选择一个值和如果我删除 EditText
中的文本以搜索其他内容,则选中复选框的位置会自动更改,如下图所示。我目前面临两个问题
1 - 勾选复选框的位置会自动改变
2 - 如果我在 EditText
中输入大写字母,ListView
将变为空白。
public class MyCustomAdapter extends BaseAdapter implements Filterable
Context mContext;
private LayoutInflater mInflater;
SparseBooleanArray mSparseBooleanArray;
private ArrayList<Map<String,String>> mAdapData = new ArrayList<Map<String, String>>();
private ArrayList<Map<String,String>> mOriginalData = new ArrayList<Map<String, String>>();
public MyCustomAdapter(Context mContext)
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSparseBooleanArray = new SparseBooleanArray();
public ArrayList<String> getCheckedItems()
ArrayList<String> mTempArry = new ArrayList<String>();
for (int i = 0; i < mAdapData.size(); i++)
if (mSparseBooleanArray.get(i))
Map<String, String> map = (Map<String, String>) mAdapData.get(i);
final String numbr = map.get("Phone").toString();
mTempArry.add(numbr);
return mTempArry;
@Override
public int getCount()
return this.mAdapData.size();
public void addItem(String paramString1, String paramString2)
Map<String, String> NameNumber = new HashMap<String, String>();
NameNumber.put("Name", paramString1);
NameNumber.put("Phone", paramString2);
this.mAdapData.add(NameNumber);
this.mOriginalData.add(NameNumber);
notifyDataSetChanged();
@SuppressWarnings("unchecked")
public Object getItem(int paramInt)
return (ArrayList<Map<String, String>>) this.mAdapData.get(paramInt);
@Override
public long getItemId(int paramInt)
return paramInt;
@Override
public View getView(final int paramInt, View paramView, ViewGroup paramViewGroup)
ViewHolder viewHolder;
if (paramView == null)
viewHolder = new ViewHolder();
paramView = mInflater.inflate(R.layout.multiplecontactview, null);
viewHolder.tvName = (TextView) paramView.findViewById(R.id.txtContactName);
viewHolder.tvNumber = (TextView) paramView.findViewById(R.id.txtContactNumber);
viewHolder.cb = (CheckBox) paramView.findViewById(R.id.checkBox1);
viewHolder.cb.setTag(paramInt);
viewHolder.cb.setChecked(mSparseBooleanArray.get(paramInt));
viewHolder.cb.setOnCheckedChangeListener(mCheckedChangeListener);
viewHolder.tvName.setTextColor(Color.BLACK);
viewHolder.tvNumber.setTextColor(Color.BLACK);
for (int i = 0; i < mAdapData.size(); i++)
Map<String, String> map = (Map<String, String>) mAdapData.get(paramInt);
final String name = map.get("Name").toString();
final String numbr = map.get("Phone").toString();
viewHolder.tvName.setText(name);
viewHolder.tvNumber.setText(numbr);
paramView.setTag(viewHolder);
else
viewHolder = (ViewHolder) paramView.getTag();
return paramView;
OnCheckedChangeListener mCheckedChangeListener = new OnCheckedChangeListener()
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
mSparseBooleanArray.put((Integer) buttonView.getTag(), isChecked);
;
public static class ViewHolder
TextView tvName;
TextView tvNumber;
CheckBox cb;
@Override
public Filter getFilter()
return new MyContactFilter();
@SuppressLint("DefaultLocale")
private class MyContactFilter extends Filter
@Override
protected FilterResults performFiltering(CharSequence constraint)
FilterResults results = new FilterResults();
ArrayList<Map<String, String>> mFilteredData = new ArrayList<Map<String, String>>();
if (!TextUtils.isEmpty(constraint))
for(int i = 0; i < mAdapData.size(); i++)
Map<String, String> map = (Map<String, String>) mAdapData.get(i);
final String names = map.get("Name").toString();
final String numbr = map.get("Phone").toString();
if(names.toLowerCase().contains(constraint.toString().toLowerCase()))
Map<String, String> FilNameNumber = new HashMap<String, String>();
FilNameNumber.put("Name", names);
FilNameNumber.put("Phone", numbr);
mFilteredData.add(FilNameNumber);
results.values = mFilteredData;
results.count = mFilteredData.size();
else
synchronized (mOriginalData)
results.values = mOriginalData;
results.count = mOriginalData.size();
return results;
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence cs, FilterResults fr)
mAdapData = (ArrayList<Map<String, String>>) fr.values;
notifyDataSetChanged();
【问题讨论】:
将选中状态存储在数据本身中。 @Singularity 请解释一下你想说什么。 【参考方案1】:摆脱复杂的映射来跟踪属性,将数据存储在这样的类中,以及检查状态:
public class Data
private final String text;
private boolean checked;
public Data(String text)
this.text = text;
this.checked = false;
public void setChecked(boolean b)
this.checked = b;
public boolean isChecked()
return this.checked;
public String getText()
return this.text;
在getView()
里面,根据数据状态设置复选框,添加监听器修改数据状态:
final Data data = getItem(position);
cb.setOnCheckedChangeListener(null); //important
cb.setChecked(data.isChecked());
cb.setOnCheckedChangeListener(new OnCheckedChangeListener()
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
data.setChecked(isChecked);
);
关于过滤:
names.toLowerCase().contains(cs)
Contains
在字符值上进行区分大小写的匹配。试试names.toLowerCase().contains(cs.toLowerCase())
【讨论】:
好的,谢谢你的建议。但你的意思是什么?Store the checked state in data itself.
@user1822826 这意味着Data
类可以存储一个String
,它是您想要在列表项中的实际文本,以及一个boolean
,它是该列表项的一个属性。使用 ArrayList<Data>
将所有 Data
对象存储在适配器中。
现在我完全明白了你想说的话。非常感谢。你能告诉我这个帖子中第二个问题的答案吗
感谢它解决了问题,但现在又出现了一个问题。如果我在EditText
中输入字母T
,列表视图中会填满以J
开头的名称,它甚至不包含字母T
显然我发现在 setOnCheckedChangeListener 之后让 cb.setChecked(data.isChecked()) 起作用。【参考方案2】:
如果您更仔细地查看您的代码,特别是在getView()
上,您会发现每次都独立于convertView
存在而创建ViewHolder
对象。
ViewHolder
模式的使用是错误的。不要每次都创建它,而是仅在convertView
为空时创建它,并将其设置为setTag()
的convertView
的标记,否则使用getTag()
获取对ViewHolder
的引用。就像这样:
ViewHolder viewHolder;
if (paramView == null)
paramView = mInflater.inflate(R.layout.multiplecontactview, null);
viewHolder = new ViewHolder();
viewHolder.tvName = (TextView) paramView.findViewById(R.id.txtContactName);
viewHolder.tvNumber = (TextView) paramView.findViewById(R.id.txtContactNumber);
viewHolder.cb = (CheckBox) paramView.findViewById(R.id.checkBox1);
paramView.setTag(viewHolder);
else
viewHolder = (ViewHolder) paramView.getTag();
viewHolder.cb.setTag(paramInt);
viewHolder.cb.setChecked(mSparseBooleanArray.get(paramInt));
viewHolder.cb.setOnCheckedChangeListener(mCheckedChangeListener);
viewHolder.tvName.setTextColor(Color.BLACK);
viewHolder.tvNumber.setTextColor(Color.BLACK);
for (int i = 0; i < mAdapData.size(); i++)
Map<String, String> map = (Map<String, String>) mAdapData.get(paramInt);
final String name = map.get("Name").toString();
final String numbr = map.get("Phone").toString();
viewHolder.tvName.setText(name);
viewHolder.tvNumber.setText(numbr);
return paramView;
同样在你的preformFiltering()
尝试改变这个sn-p:
if (names.toLowerCase().contains(cs))
到
if (names.toLowerCase().contains(cs.toString().toLowerCase()))
更新
有一些奇怪的sn-p代码:
OnCheckedChangeListener mCheckedChangeListener = new OnCheckedChangeListener()
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
mSparseBooleanArray.put((Integer) buttonView.getTag(), isChecked);
;
这意味着如果你第一次选中列表中第 0 位的列表项,SparseArray
表示ListView
的第一项应该总是有一个复选框,因为你没有在代码中的任何地方取消选中它,不要将值设置为 false,但只有在用户手动取消选中复选框时才这样做。
【讨论】:
根据您编辑的答案进行更改后,问题又开始了。 那么过滤大写字母呢? 这个问题现在已经解决了。但重新过滤数据后勾选的复选框仍在更改位置 我不完全明白你所说的“改变位置”是什么意思。是否停留在ListView
的首位?
嗨 @AlexBonel 它对我来说很有效,但是当我清除 Serach 框时,复选框未选中以上是关于过滤Listview数据后勾选(选中)复选框的位置发生变化的主要内容,如果未能解决你的问题,请参考以下文章