过滤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&lt;Data&gt; 将所有 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数据后勾选(选中)复选框的位置发生变化的主要内容,如果未能解决你的问题,请参考以下文章

jQuery实现全选效果

如何获取listview里选中的checkbox

C# winform listview 选中的复选框排序

jQuery selectable() 不响应复选框勾选

根据复选框值过滤列表

滚动自定义 ListView 时,复选框值发生变化