Android 中使用ListView和CheckBox进行批量操作

在使用ListView时,一般为了性能的提升,都会使用ViewHolder,也就是Item的View实现复用。

现在的问题是,当在ListView的Item中包含CheckBox,并且CheckBox的事件处理监听器是holder.checkbox.setOnCheckedChangeListener()时,会出现第一项开始未选中,当第二项选中时第一项也跟着选中,这显然不是我们想要的结果。

出现这个问题的原因是第一项和第二项用的是同一个Item,当第二项选中时,CheckBox的当前状态为选中,这时setOnCheckedChangeListener里面会改变第一项关联的实体对象的属性(引用类型,变量A、B都引用同一个对象AA,当A把AA的某个属性值修改了,B再次访问时,AA对象的那个属性的值为A引用改后的值),代码如下:

1holder.checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
2  
3            @Override
4            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
5                driver.setSelected(isChecked);
6            }
7  
8        });

解决办法:

1、在ListViewAdapter初始化时,将对象中有关CheckBox是否选中的属性存储起来。

1selectedMap = new HashMap<Integer, Boolean>(); 
2      int size = mPersons.size();
3      for (int i = 0; i < size; i++) { 
4          selectedMap.put(i, mPersons.get(i).isSelected()); 
5      }

2、去掉CheckBox的holder.checkbox.setOnCheckedChangeListener(){}事件监听器

3、在Adapter里的 public View getView(final int position, View convertView, ViewGroup parent){}方法体里面,当前的CheckBox是否选中状态,由之前初始化时保存的对象属性值控制,代码如下:

1boolean selected = selectedMap.get(position);
2holder.checkbox.setChecked(selected);

3、用户点击ListView的Item时,改变CheckBox的状态,代码如下:

1convertView.setOnClickListener(new View.OnClickListener() {
2 
3           @Override
4           public void onClick(View v) {
5               checkbox.toggle();
6               selectedMap.put(position, checkbox.isChecked()); 
7               driver.setSelected(checkbox.isChecked());
8           }
9       });

数据适配器ListViewAdapter的完整代码:

001package com.easipass.cloud.ccp.adapter;
002  
003import java.util.ArrayList;
004import java.util.HashMap;
005  
006import android.content.Context;
007import android.view.LayoutInflater;
008import android.view.View;
009import android.view.ViewGroup;
010import android.widget.BaseAdapter;
011import android.widget.CheckBox;
012import android.widget.Filter;
013import android.widget.Filterable;
014import android.widget.ImageView;
015import android.widget.TextView;
016  
017import com.easipass.R;
018import com.easipass.cloud.ccp.entity.UserInfo;
019  
020/**
021 * 用户列表数据适配器
022 
023 * @author android_ls
024 */
025public final class UserListViewAdapter extends BaseAdapter implements Filterable {
026    private LayoutInflater inflater;
027  
028    private MyFilter myFilter;
029  
030    private final Object mLock = new Object();
031  
032    private ArrayList<UserInfo> mPersons;
033  
034    private ArrayList<UserInfo> mCheckValues;
035  
036    public HashMap<Integer, Boolean> selectedMap;
037  
038    public UserListViewAdapter(Context context, ArrayList<UserInfo> cms) {
039        inflater = LayoutInflater.from(context);
040        mPersons = cms;
041  
042        selectedMap = new HashMap<Integer, Boolean>();
043        int size = mPersons.size();
044        for (int i = 0; i < size; i++) {
045            selectedMap.put(i, mPersons.get(i).isSelected());
046        }
047  
048    }
049  
050    @Override
051    public int getCount() {
052        return mPersons.size();
053    }
054  
055    @Override
056    public Object getItem(int arg0) {
057        return mPersons.get(arg0);
058    }
059  
060    @Override
061    public long getItemId(int position) {
062        return position;
063    }
064  
065    @Override
066    public View getView(final int position, View convertView, ViewGroup parent) {
067        ViewHolder holder = null;
068        if (convertView == null) {
069            convertView = inflater.inflate(R.layout.ccp_carmanager_lv_item, null);
070            holder = new ViewHolder();
071            holder.text1 = (TextView) convertView.findViewById(R.id.tv_name);
072            holder.text2 = (TextView) convertView.findViewById(R.id.tv_phnoe);
073            holder.checkbox = (CheckBox) convertView.findViewById(R.id.checkbox);
074            holder.imageView = (ImageView) convertView.findViewById(R.id.iv_icon);
075  
076            convertView.setTag(holder);
077        } else {
078            holder = (ViewHolder) convertView.getTag();
079        }
080  
081        final UserInfo driver = mPersons.get(position);
082  
083        holder.text1.setText(driver.getName());
084  
085        holder.text2.setText(driver.getPhoneNumber());
086        // TODO 测试
087        holder.imageView.setBackgroundResource(Integer.valueOf(driver.getIconUrl()));
088        holder.checkbox.setVisibility(View.VISIBLE);
089  
090        boolean selected = selectedMap.get(position);
091        holder.checkbox.setChecked(selected);
092  
093        final CheckBox checkbox = holder.checkbox;
094        convertView.setOnClickListener(new View.OnClickListener() {
095  
096            @Override
097            public void onClick(View v) {
098                checkbox.toggle();
099                selectedMap.put(position, checkbox.isChecked());
100                driver.setSelected(checkbox.isChecked());
101            }
102        });
103  
104        if (selected) {
105            convertView.setClickable(false);
106        }
107  
108        return convertView;
109    }
110  
111    @Override
112    public Filter getFilter() {
113        if (myFilter == null) {
114            myFilter = new MyFilter();
115        }
116        return myFilter;
117    }
118  
119    class MyFilter extends Filter {
120  
121        @Override
122        protected FilterResults performFiltering(CharSequence prefix) {
123            FilterResults results = new FilterResults();
124            if (mCheckValues == null) {
125                synchronized (mLock) {
126                    mCheckValues = new ArrayList<UserInfo>(mPersons);
127                }
128            }
129  
130            if (prefix == null || prefix.length() == 0) {
131                synchronized (mLock) {
132                    ArrayList<UserInfo> list = new ArrayList<UserInfo>(mCheckValues);
133                    results.values = list;
134                    results.count = list.size();
135                }
136            } else {
137                String prefixString = prefix.toString().toLowerCase();
138                final ArrayList<UserInfo> values = mCheckValues;
139                final int count = values.size();
140  
141                final ArrayList<UserInfo> newValues = new ArrayList<UserInfo>(count);
142                for (int i = 0; i < count; i++) {
143                    final UserInfo value = (UserInfo) values.get(i);
144                    if (value.getName().contains(prefixString)) {
145                        newValues.add(value);
146                    }
147                }
148  
149                results.values = newValues;
150                results.count = newValues.size();
151            }
152  
153            return results;
154        }
155  
156        @SuppressWarnings("unchecked")
157        @Override
158        protected void publishResults(CharSequence constraint, FilterResults results) {
159            mPersons = (ArrayList<UserInfo>) results.values;
160            if (results.count > 0) {
161                notifyDataSetChanged();
162            } else {
163                notifyDataSetInvalidated();
164            }
165        }
166    }
167  
168    static class ViewHolder {
169        public TextView text1;
170  
171        public TextView text2;
172  
173        public ImageView imageView;
174  
175        public CheckBox checkbox;
176    }
177  
178}

Activity中onCreate()里的写法:

1mSearchToolbar = (SearchToolbar) this.findViewById(R.id.top_search_toolbar);
2   mListView = (ListView) this.findViewById(R.id.listview);
3     
4   mDriverListAdapter = new UserListViewAdapter(this, driverList);
5   mListView.setAdapter(mDriverListAdapter);
6 
7   mSearchToolbar.setFilter(mDriverListAdapter.getFilter());

SearchToolbar类的代码:

01package com.easipass.custom.view;
02import android.content.Context;
03import android.text.Editable;
04import android.text.TextWatcher;
05import android.util.AttributeSet;
06import android.view.LayoutInflater;
07import android.view.View;
08import android.widget.AutoCompleteTextView;
09import android.widget.FrameLayout;
10import android.widget.ImageView;
11import android.widget.RelativeLayout;
12  
13import com.easipass.R;
14  
15/**
16 * 功能描述:自定义搜索框组件
17 * @author android_ls
18 */
19public class SearchToolbar extends FrameLayout {
20  
21    private RelativeLayout topSearchToolbar;
22      
23    /**
24     * 顶部自动补全文本输入框
25     */
26    private AutoCompleteTextView autoSearch;
27  
28    /**
29     * 清除搜索结果按钮
30     */
31    private ImageView btnClearSearch;
32      
33    public SearchToolbar(Context context) {
34        super(context);
35        setupViews();
36    }
37  
38    public SearchToolbar(Context context, AttributeSet attrs) {
39        super(context, attrs);
40        setupViews();
41    }
42  
43    private void setupViews() {
44        final LayoutInflater mLayoutInflater = LayoutInflater.from(getContext());
45        topSearchToolbar = (RelativeLayout) mLayoutInflater.inflate(R.layout.top_search_toolbar, null);
46        addView(topSearchToolbar);
47          
48        btnClearSearch = (ImageView) topSearchToolbar.findViewById(R.id.iv_search_clear);
49        autoSearch = (AutoCompleteTextView) topSearchToolbar.findViewById(R.id.auto_search);
50    }
51      
52    public void setFilter(final  android.widget.Filter filter) {
53        autoSearch.addTextChangedListener(new TextWatcher() {
54  
55            @Override
56            public void onTextChanged(CharSequence s, int start, int before, int count) {
57                String filterWord = autoSearch.getText().toString().trim();
58                filter.filter(filterWord);
59            }
60  
61            @Override
62            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
63                // TODO Auto-generated method stub
64  
65            }
66  
67            @Override
68            public void afterTextChanged(Editable s) {
69                // TODO Auto-generated method stub
70  
71            }
72        });
73  
74        btnClearSearch.setOnClickListener(new View.OnClickListener() {
75  
76            @Override
77            public void onClick(View v) {
78                autoSearch.setText(null);
79            }
80        });
81    }
82      
83}

top_search_toolbar.xml文件:

01<?xml version="1.0" encoding="utf-8"?>
02<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
03    android:id="@+id/top_search_bar"
04    android:layout_width="match_parent"
05    android:layout_height="45dip"
06    android:background="@drawable/search_bar_bg"
07    android:visibility="visible" >
08    <AutoCompleteTextView
09        android:id="@+id/auto_search"
10        android:layout_width="fill_parent"
11        android:layout_height="wrap_content"
12        android:layout_centerVertical="true"
13        android:layout_marginLeft="5dip"
14        android:layout_marginRight="5dip"
15        android:background="@drawable/search_bar_edit_normal"
16        android:completionThreshold="1"
17        android:drawableLeft="@drawable/search_bar_icon_normal"
18        android:dropDownHorizontalOffset="30dip"
19        android:dropDownVerticalOffset="9dip"
20        android:dropDownWidth="210dip"
21        android:singleLine="true"
22        android:textSize="15sp" />
23  
24    <ImageView
25        android:id="@+id/iv_search_clear"
26        android:layout_width="wrap_content"
27        android:layout_height="wrap_content"
28        android:layout_alignParentRight="true"
29        android:layout_centerVertical="true"
30        android:layout_marginRight="12dip"
31        android:layout_marginTop="-1dip"
32        android:background="@drawable/btn_search_clear_selector" />
33  
34</RelativeLayout>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值