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

jopen 12年前

在使用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>


  来自:http://blog.csdn.net/android_ls/article/details/8644247