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

来源:互联网 发布:八字预测全息论淘宝 编辑:程序博客网 时间:2024/05/21 10:22

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

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

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

holder.checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {            @Override            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {                driver.setSelected(isChecked);            }        });

解决办法:

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

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

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

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

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

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

 convertView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                checkbox.toggle();                selectedMap.put(position, checkbox.isChecked());                 driver.setSelected(checkbox.isChecked());            }        });

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

package com.easipass.cloud.ccp.adapter;import java.util.ArrayList;import java.util.HashMap;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.CheckBox;import android.widget.Filter;import android.widget.Filterable;import android.widget.ImageView;import android.widget.TextView;import com.easipass.R;import com.easipass.cloud.ccp.entity.UserInfo;/** * 用户列表数据适配器 *  * @author android_ls */public final class UserListViewAdapter extends BaseAdapter implements Filterable {    private LayoutInflater inflater;    private MyFilter myFilter;    private final Object mLock = new Object();    private ArrayList<UserInfo> mPersons;    private ArrayList<UserInfo> mCheckValues;    public HashMap<Integer, Boolean> selectedMap;    public UserListViewAdapter(Context context, ArrayList<UserInfo> cms) {        inflater = LayoutInflater.from(context);        mPersons = cms;        selectedMap = new HashMap<Integer, Boolean>();        int size = mPersons.size();        for (int i = 0; i < size; i++) {            selectedMap.put(i, mPersons.get(i).isSelected());        }    }    @Override    public int getCount() {        return mPersons.size();    }    @Override    public Object getItem(int arg0) {        return mPersons.get(arg0);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(final int position, View convertView, ViewGroup parent) {        ViewHolder holder = null;        if (convertView == null) {            convertView = inflater.inflate(R.layout.ccp_carmanager_lv_item, null);            holder = new ViewHolder();            holder.text1 = (TextView) convertView.findViewById(R.id.tv_name);            holder.text2 = (TextView) convertView.findViewById(R.id.tv_phnoe);            holder.checkbox = (CheckBox) convertView.findViewById(R.id.checkbox);            holder.imageView = (ImageView) convertView.findViewById(R.id.iv_icon);            convertView.setTag(holder);        } else {            holder = (ViewHolder) convertView.getTag();        }        final UserInfo driver = mPersons.get(position);        holder.text1.setText(driver.getName());        holder.text2.setText(driver.getPhoneNumber());        // TODO 测试        holder.imageView.setBackgroundResource(Integer.valueOf(driver.getIconUrl()));        holder.checkbox.setVisibility(View.VISIBLE);        boolean selected = selectedMap.get(position);        holder.checkbox.setChecked(selected);        final CheckBox checkbox = holder.checkbox;        convertView.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                checkbox.toggle();                selectedMap.put(position, checkbox.isChecked());                driver.setSelected(checkbox.isChecked());            }        });        if (selected) {            convertView.setClickable(false);        }        return convertView;    }    @Override    public Filter getFilter() {        if (myFilter == null) {            myFilter = new MyFilter();        }        return myFilter;    }    class MyFilter extends Filter {        @Override        protected FilterResults performFiltering(CharSequence prefix) {            FilterResults results = new FilterResults();            if (mCheckValues == null) {                synchronized (mLock) {                    mCheckValues = new ArrayList<UserInfo>(mPersons);                }            }            if (prefix == null || prefix.length() == 0) {                synchronized (mLock) {                    ArrayList<UserInfo> list = new ArrayList<UserInfo>(mCheckValues);                    results.values = list;                    results.count = list.size();                }            } else {                String prefixString = prefix.toString().toLowerCase();                final ArrayList<UserInfo> values = mCheckValues;                final int count = values.size();                final ArrayList<UserInfo> newValues = new ArrayList<UserInfo>(count);                for (int i = 0; i < count; i++) {                    final UserInfo value = (UserInfo) values.get(i);                    if (value.getName().contains(prefixString)) {                        newValues.add(value);                    }                }                results.values = newValues;                results.count = newValues.size();            }            return results;        }        @SuppressWarnings("unchecked")        @Override        protected void publishResults(CharSequence constraint, FilterResults results) {            mPersons = (ArrayList<UserInfo>) results.values;            if (results.count > 0) {                notifyDataSetChanged();            } else {                notifyDataSetInvalidated();            }        }    }    static class ViewHolder {        public TextView text1;        public TextView text2;        public ImageView imageView;        public CheckBox checkbox;    }}

Activity中onCreate()里的写法:

     mSearchToolbar = (SearchToolbar) this.findViewById(R.id.top_search_toolbar);        mListView = (ListView) this.findViewById(R.id.listview);                mDriverListAdapter = new UserListViewAdapter(this, driverList);        mListView.setAdapter(mDriverListAdapter);        mSearchToolbar.setFilter(mDriverListAdapter.getFilter());

SearchToolbar类的代码:

package com.easipass.custom.view;import android.content.Context;import android.text.Editable;import android.text.TextWatcher;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.widget.AutoCompleteTextView;import android.widget.FrameLayout;import android.widget.ImageView;import android.widget.RelativeLayout;import com.easipass.R;/** * 功能描述:自定义搜索框组件 * @author android_ls */public class SearchToolbar extends FrameLayout {    private RelativeLayout topSearchToolbar;        /**     * 顶部自动补全文本输入框     */    private AutoCompleteTextView autoSearch;    /**     * 清除搜索结果按钮     */    private ImageView btnClearSearch;        public SearchToolbar(Context context) {        super(context);        setupViews();    }    public SearchToolbar(Context context, AttributeSet attrs) {        super(context, attrs);        setupViews();    }    private void setupViews() {        final LayoutInflater mLayoutInflater = LayoutInflater.from(getContext());        topSearchToolbar = (RelativeLayout) mLayoutInflater.inflate(R.layout.top_search_toolbar, null);        addView(topSearchToolbar);                btnClearSearch = (ImageView) topSearchToolbar.findViewById(R.id.iv_search_clear);        autoSearch = (AutoCompleteTextView) topSearchToolbar.findViewById(R.id.auto_search);    }        public void setFilter(final  android.widget.Filter filter) {        autoSearch.addTextChangedListener(new TextWatcher() {            @Override            public void onTextChanged(CharSequence s, int start, int before, int count) {                String filterWord = autoSearch.getText().toString().trim();                filter.filter(filterWord);            }            @Override            public void beforeTextChanged(CharSequence s, int start, int count, int after) {                // TODO Auto-generated method stub            }            @Override            public void afterTextChanged(Editable s) {                // TODO Auto-generated method stub            }        });        btnClearSearch.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                autoSearch.setText(null);            }        });    }    }

top_search_toolbar.xml文件:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/top_search_bar"    android:layout_width="match_parent"    android:layout_height="45dip"    android:background="@drawable/search_bar_bg"    android:visibility="visible" >    <AutoCompleteTextView        android:id="@+id/auto_search"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:layout_centerVertical="true"        android:layout_marginLeft="5dip"        android:layout_marginRight="5dip"        android:background="@drawable/search_bar_edit_normal"        android:completionThreshold="1"        android:drawableLeft="@drawable/search_bar_icon_normal"        android:dropDownHorizontalOffset="30dip"        android:dropDownVerticalOffset="9dip"        android:dropDownWidth="210dip"        android:singleLine="true"        android:textSize="15sp" />    <ImageView        android:id="@+id/iv_search_clear"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:layout_centerVertical="true"        android:layout_marginRight="12dip"        android:layout_marginTop="-1dip"        android:background="@drawable/btn_search_clear_selector" /></RelativeLayout>