實現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的顯示狀態會亂跳,但是你實際上選擇的還是第一條數據,只是會讓你的用戶感覺很不爽罷



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章