ListView的View回收引起的checkbox狀態改變監聽問題

來源:互聯網
上載者:User

之前講到了自訂Adapter傳遞給ListView時,因為ListView的View回收,需要注意當ListView清單項目中包含有帶有狀態標識控制項的問題。詳情可見之前發的帖[url=自訂Adapter實現ListView帶多選框等狀態控制項的注意事項 http://www.eoeandroid.com/thread-248749-1-1.html
還是這個問題,講一個我遇到的因為兩行代碼位置相反引起的問題。

我的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項資料。如果我把兩行代碼順序反過來,先更改監聽器,再設定狀態,引發的監聽器自然也就是新的監聽器,邏輯也就對了。

 

原文:http://www.eoeandroid.com/thread-249189-1-1.html

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.