Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> ListView的View回收引起的checkbox狀態改變監聽等問題解決方案

ListView的View回收引起的checkbox狀態改變監聽等問題解決方案

編輯:Android開發實例

之前講到了自定義Adapter傳遞給ListView時,因為ListView的View回收,需要注意當ListView列表項中包含有帶有狀態標識控件的問題。詳情可見之前發的帖[url=自定義Adapter實現ListView帶多選框等狀態控件的注意事項 http://www.jb51.net/article/33425.htm
還是這個問題,講一個我遇到的因為兩行代碼位置相反引起的問題。
我的ListView中每行View包含一個ImageView、TextView、CheckBox。當ListView中有一個或一個一行CheckBox被選中就讓ListView上面的Button顯示,否則就隱藏。因此,需要對每行View中的CheckBox設置監聽。我使用CheckBox中的OnCheckedChangeListener監聽器,當CheckBox的狀態發生改變的時候就會觸發這個監聽器。先看下我自定義給ListView的Adapter的getView方法中的一些關鍵代碼:
這是getView方法中使用到的內部類:
代碼如下:

static class ViewHolder {
public ImageView imageView;
public TextView textView;
public CheckBox checkBox;
}

這是getView方法中利用ListView回收機制循環利用View的代碼:
代碼如下:

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.searchitem, null);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) convertView
.findViewById(R.id.searchitemimage);
viewHolder.textView = (TextView) convertView
.findViewById(R.id.searchitemtext);
viewHolder.checkBox = (CheckBox) convertView
.findViewById(R.id.searchitemcheckbox);
convertView.setTag(viewHolder);
} else {
// Log.i(CodeUtils.SEARCHTAG, "view is reuse");
viewHolder = (ViewHolder) convertView.getTag();
}

接下來是對其中checkbox設置顯示狀態和監聽器的代碼:
代碼如下:

viewHolder.checkBox
.setOnCheckedChangeListener(new SearchItemOnCheckedChangeListener(
position, state));
viewHolder.checkBox.setChecked(state[position]);

之前說過了,因為ListView的回收,需要使用一個數組或list來記錄每項數據中checkbox的狀態。這裡,state是與ListView列表等長的boolean數組,用於記錄每個position(也就是每個列表項數據的id)標識的數據上checkbox應該顯示的狀態,初始的狀態都是false。構造checkbox監聽器的時候需要傳遞當前View的position,以及整個列表checkbox的狀態數組state。以下是checkBox狀態改變監聽器的代碼:
代碼如下:

public class SearchItemOnCheckedChangeListener implements
OnCheckedChangeListener {
private int id;
private Boolean[] state;
public SearchItemOnCheckedChangeListener(int id, Boolean[] state) {
this.id = id;
this.state = state;
}
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
state[id] = isChecked;
if (isChecked) {
checkedCount++;
}else{
checkedCount--;
}
if (checkCoutn>0) {
searchButton.setVisibility(Button.INVISIBLE);
} else {
searchButton.setVisibility(Button.VISIBLE);
}
}
}
}

這裡面checkedCount初始值為0的整型,用於記錄被選中多選框的數量。searchButton是根據checkbox而決定顯示還是隱藏的按鈕。

以上整個邏輯功能的實現代碼。開頭說了,這是一個我因為ListView的回收機制和兩行代碼位置相反引起的問題。兩行代碼的位置相反將導致完全不同的結果,所指的就是設置checkbox監聽器和狀態的兩行代碼,起初我的順序為:
代碼如下:

viewHolder.checkBox.setChecked(state[position]);
viewHolder.checkBox.setOnCheckedChangeListener(new SearchItemOnCheckedChangeListener(position, state));

這樣的順序出現的問題是,當我拉動列表後,因為拉動被隱藏的列表項狀態將被更改為false。這很不可思議,因為我已經分離了一個狀態數組來記錄每個checkbox的狀態,想來想去只有一個可能,就是狀態數組中的值改變了,而改變狀態數組的值位置就在於OnCheckedChangeListener中。Debug了幾個小時,才想通了問題就在於這兩行代碼為位置順序。

起因還是得講到ListView的回收機制。假如我的ListView最多只能顯示10個View,那麼起初就會調用十次getView構造十個全新的View(包括對其中的checkbox設置監聽器)。當我將列表往下拉出現第11個列表項的時候,頂部第一個列表項被隱藏,同樣會再調用一次getView,不過此時getView的參數將返回剛剛被隱藏的第一個列表項的View,並對這個View更改數據作為即將出現的第11個View。問題就出在這裡,我把checkbox.setChecked()方法調用放在了設置監聽器前面,此時因為更改了checkbox的狀態,勢必引起觸發狀態更改的監聽器。注意!由於第11個View是用被隱藏的第1個View回收來的,雖然還沒有執行下一行設置監聽器的代碼,但實際上它已經擁有了一個狀態監聽器,這個監聽器是這個View還是作為第一個View時設置。那個時候的監聽器設置更改的第一項的數據,而不是第11項數據。因此,理所當然不能正確更改第11項數據,反而更改了無辜的第1項數據。如果我把兩行代碼順序反過來,先更改監聽器,再設置狀態,引發的監聽器自然也就是新的監聽器,邏輯也就對了。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved