首页 > 代码库 > 实现ListView带多选框等状态控件实现Adapter出现紊乱

实现ListView带多选框等状态控件实现Adapter出现紊乱

Android本身为ListView提供了几个方便的Adapter,比如ArrayAdapter、SimpleCurrentAdapter等等。但是为了实现更复杂的列表视图和控制,一般都要继承BaseAdapter来实现自己的Adapter。

我需要的ListView是如图一样的列表,每个列表项由一个TextView、3个CheckBox组成,3个checkbox选中为红色图片,为选中为白色背景图片因此需要对每个列表项的CheckBox设置监听器。若使用Android提供的Adapter实现起来比较复杂,所以我选择继承BaseAdapter来实现自己的Adapter。


首先要知道的是ListView显示原理。ListView接收了Adapter之后,ListView的列表项数据是来源于接收的Adapter的。当ListView要展现出来的时候,ListView就会调用Adapter的getCount方法来获得一共需要绘制多少个列表项,之后就开始调用getView方法来获得每个列表项的View进行装载。也就是说ListView的列表项就是每次调用getView返回的View,每次调用getView获得的列表项View是什么样子,我们看到的这个列表项就是什么样子。

我继承BaseAdapter来实现自己的Adapter,至少需要重写基本的getView、getCount、getItem、getItemID四个方法。其中getCount和getView的功能如上所述,所以我要想实现对每个列表项多选框按钮的监听就需要在getView中返回View给ListView之前,对View中的多选框设置监听器。getView方法中带三个参数public View getView(int position,View convertView,ViewGroup parent),一般都是将convertView最为返回的View。

在这里,需要插播说明一下Android系统对ListView的实现小细节。Android构造ListView列表项的时候每次只会构造足够满足屏幕显示数量的列表项,一般都是10个左右。当ListView的列表项多于屏幕能够显示的列表项的时候,ListView就可以上下拉动,每次拉动显示后续列表项时就会再次调用getView方法来构造后续列表项的View。如果ListView是首次显示出来,那么getView的参数View convertView就是null空的;如果是拉动ListView调用的getView,那么这时getView的参数convertView就不再是null,而是随着拉动刚刚被拉走隐藏掉的列表项的View。这么做的好处是可以节省资源。

我的需求中需要对多选框进行监听,所以在返回convertView之前需要中convertView中获取多选框控件并设置监听器。一开始,我以为对每个chekbox设置监听就能够实现我的需求了,但是出来的结果却意想不到。当我点击一个多选框后,将列表往下拉,下面出来的没有选中的列表框也变成选中的状态,并且上面原本被选中的状态恢复到为选中状态。注意到我每次点击一个多选框后往下拉同步被选中的多选框的距离都是不变的,总是相隔11项。于是,回想getView中convertView参数的特点,当我往下拉的时候,ListView调用getView方法中的convertView就是回收来的因为拉动被隐藏的View。在我这个例子中,由于多选框是一种带有状态标示的控件,我的getView没有对其状态进行重新设置,所以就造成了这种奇怪现象。我的解决方法是在我实现的Adapter类中创建一个boolean数组用于保存对应列表项多选框的状态(getView中第一个参数position就是列表项ID,是根据数据来标识,不是根据列表项View来标识的,所以可以根据position来对列表项数据进行选中和非选中标识),每次调用getView都会在其中判断position位置上的boolean值来决定多选框的状态。

同样的,基于这个原理,使用其它带状态的控件也需要注意getView回收的问题。当然,也可以不使用convertView最为getView的返回结果,而在getView每次调用都重新构造一个View,或者Adapter类中构造一个与数据数量等长的View数组。不过这么做的话就比较消耗资源。

另外,BaseAdapter中的getItem和getItemId方法再构造ListView构造过程中并没有使用过,不过据说是在关于ListView的一些监听器中会调用到,所以继承BaseAdapter时最好也给这两个方法返回一个有意义的值。getItemId一般返回对应的position,getItem返回对应position的列表数据对象。

代码:adapte

package com.bhi.monthlycycles.util;


import java.util.ArrayList;

import java.util.HashMap;

import java.util.Map;


import com.bhi.monthlycycles.MyApplication;

import com.bhi.monthlycycles.R;

import com.bhi.monthlycycles.database.MyDataBaseUtil;

import com.bhi.monthlycycles.database.ZMOODS;

import com.bhi.monthlycycles.database.ZRECORDDISPLAY;


import android.content.Context;

import android.util.Log;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.widget.BaseAdapter;

import android.widget.CheckBox;

import android.widget.CompoundButton;

import android.widget.CompoundButton.OnCheckedChangeListener;

import android.widget.TextView;


public class SymptomListViewAdapterextends BaseAdapter {


private Context mContext;

private ArrayList<ZRECORDDISPLAY>recordDisList ;

private MyDataBaseUtil myDataBaseUtil;

private HashMap<String, Integer>moodShowMap =new HashMap<String, Integer>();//存放图片的id

private ArrayList<ZRECORDDISPLAY>myRecordDisList =new ArrayList<ZRECORDDISPLAY>();

public ArrayList<String> isCheckedList = new ArrayList<String>();

public HashMap<String, Integer>symptomValue =new HashMap<String, Integer>();// 保存symptom的选中的值

private String[]sympDisplayData={"Ance","Cramps","Headaches","Bachaches","Bloating","Bodyaches","Nausea","Tender Breasts",

"Spotting","Period Pain","Dizziness","Cold","Sleepless","Flatulence","Diarrhea","Constipation","Ferver"};

private String strName;

// private CheckBox checkBox1,checkBox2,checkBox3;

public Map<Integer,Boolean> checkedMap1;   //保存checkbox是否被选中的状态 

public Map<Integer,Boolean> checkedMap2;   //保存checkbox是否被选中的状态 

public Map<Integer,Boolean> checkedMap3;   //保存checkbox是否被选中的状态 

public SymptomListViewAdapter() {

super();

// TODO Auto-generated constructor stub

}


public SymptomListViewAdapter(Context mContext) {

this.mContext = mContext;

myDataBaseUtil = new MyDataBaseUtil(mContext);

recordDisList = myDataBaseUtil.selectZRECORDDISPLAY();

for(int i=0;i<recordDisList.size();i++){

if(recordDisList.get(i).getZISENABLE() == 0){ // "0" 显示 , "1" 不显示

myRecordDisList.add(recordDisList.get(i));

}

}

for(int j=0;j<sympDisplayData.length;j++){

symptomValue.put(sympDisplayData[j], 0);

}

checkedMap1 = new HashMap<Integer, Boolean>();  

checkedMap2 = new HashMap<Integer, Boolean>()

checkedMap3 = new HashMap<Integer, Boolean>()

        for(int i=0;i<myRecordDisList.size();i++){  

            checkedMap1.put(i, false); 

            checkedMap2.put(i, false);

            checkedMap3.put(i, false);

        }  

}


@Override

public int getCount() {

// TODO Auto-generated method stub

returnmyRecordDisList.size();

}


@Override

public Object getItem(int position) {

// TODO Auto-generated method stub

return myRecordDisList.get(position);

}


@Override

public long getItemId(int position) {

// TODO Auto-generated method stub

return position;

}


@Override

public View getView(finalint position, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

// if(convertView == null){

convertView = LayoutInflater.from(mContext).inflate(R.layout.symptom_chioce_item,null);

// }

TextView symptomTextView = (TextView) convertView.findViewById(R.id.symptom_textView1);

final CheckBox checkBox1 = (CheckBox) convertView.findViewById(R.id.symptom_chebox1);

final CheckBox checkBox2 = (CheckBox) convertView.findViewById(R.id.symptom_chebox2);

final CheckBox checkBox3 = (CheckBox) convertView.findViewById(R.id.symptom_chebox3);

checkBox1.setChecked(checkedMap1.get(position)); 

checkBox2.setChecked(checkedMap2.get(position));

checkBox3.setChecked(checkedMap3.get(position));

strName = myRecordDisList.get(position).getZRECORDNAME();

symptomTextView.setText(strName);

checkBox1.setBackgroundResource(MyApplication.symptomImgsSelector.get(strName).get(0));

checkBox2.setBackgroundResource(MyApplication.symptomImgsSelector.get(strName).get(1));

checkBox3.setBackgroundResource(MyApplication.symptomImgsSelector.get(strName).get(2));

// 只有一个选项

if(strName.equals("Spotting") ||strName.equals("Sleepless") ||strName.equals("Flatulence")

|| strName.equals("Diarrhea") || strName.equals("Constipation") ||strName.equals("Ferver")){

checkBox1.setVisibility(View.INVISIBLE);

checkBox3.setVisibility(View.INVISIBLE);

checkBox2.setOnCheckedChangeListener(new OnCheckedChangeListener() {

@Override

publicvoid onCheckedChanged(CompoundButton buttonView,boolean isChecked) {

//TODO Auto-generated method stub

if(isChecked){

checkBox1.setChecked(false);

checkBox3.setChecked(false);

symptomValue.put(strName, 2);

checkedMap2.put(position,true);

}else{

symptomValue.put(strName, 0);

checkedMap2.put(position,false);

}

}

});

}else// 三个选项

checkBox1.setVisibility(View.VISIBLE);

checkBox2.setVisibility(View.VISIBLE);

checkBox3.setVisibility(View.VISIBLE);

checkBox1.setOnCheckedChangeListener(new OnCheckedChangeListener() {

@Override

publicvoid onCheckedChanged(CompoundButton buttonView,boolean isChecked) {

//TODO Auto-generated method stub

if(isChecked){

checkBox2.setChecked(false);

checkBox3.setChecked(false);

symptomValue.put(strName, 1);

checkedMap1.put(position,true);

}else{

symptomValue.put(strName, 0);

checkedMap1.put(position,false);

}

}

});

checkBox2.setOnCheckedChangeListener(new OnCheckedChangeListener() {

@Override

publicvoid onCheckedChanged(CompoundButton buttonView,boolean isChecked) {

//TODO Auto-generated method stub

if(isChecked){

checkBox1.setChecked(false);

checkBox3.setChecked(false);

symptomValue.put(strName, 2);

checkedMap2.put(position,true);

}else{

symptomValue.put(strName, 0);

checkedMap2.put(position,false);

}

}

});

checkBox3.setOnCheckedChangeListener(new OnCheckedChangeListener() {

@Override

publicvoid onCheckedChanged(CompoundButton buttonView,boolean isChecked) {

//TODO Auto-generated method stub

if(isChecked){

checkBox1.setChecked(false); 

checkBox2.setChecked(false);

symptomValue.put(strName, 3);

checkedMap3.put(position,true);

}else{

symptomValue.put(strName, 0);

checkedMap3.put(position,false);

}

}

});

}

return convertView;

}


}


这个与ListView的刷新机制有关,当你的listview对象很多的时候,每次你拖动listview上下滚动,listview都会刷新一次。怎么刷新呢?比如一个屏幕它最多只显示七条listview,如果你有十条数据,当你想看第八条时,第一条数据理所当然的要被隐藏掉,而第八条数据会被显示,这时listview就刷新了。如果你不保存你所选的checkbox的状态,这时如果你选的是第一条的checkbox的状态为true,当你把余下的第八、第九、第十条数据显示出来时,第十条的checkbox的状态会显示为true,但是它的状态没有被保存,只是你看到它是被选中了而已,其实你选的还是第一条数据。这个问题很操蛋。还有一个更离奇的状态,你让checkbox的状态为true,数据一定要大于十条,你不停的上下拖动屏幕,你会看见checkbox的显示状态会乱跳,但是你实际上选择的还是第一条数据,只是会让你的用户感觉很不爽罢



实现ListView带多选框等状态控件实现Adapter出现紊乱