2017年10月30日 星期一

Android ListView onCheckedChange 非預期的呼叫

最近在 ListView item 中放入 checkbox,然後用 notifyDataSetChanged 改變 ListView,但是發現 checkbox 的 onCheckedChange 會非預期的被呼叫到

例如在第三個 item 將 checkbox 勾選,然後將儲存在 Adapter 中的 data set 更新,更新後的第三個 item checkbox 預設是沒勾選的狀態,則在呼叫 notifyDataSetChanged 時就會 call 到 第三個 item checkbox 的 onCheckedChange

調查後發現,我使用 setViewBinder 來初始化 checkbox 的勾選狀態,但是在 setChecked 之前先用 setOnCheckedChangeListener 設定了 OnCheckedChangeListener 
@Override
public boolean setViewValue(View view, Object o, String s) {
    // Not correct!!
    AppCompatCheckBox cb = (AppCompatCheckBox)view;
    cb.setOnCheckedChangeListener(listener);
    cb.setChecked((boolean)o);

    // Correct
    cb.setOnCheckedChangeListener(null);
    AppCompatCheckBox cb = (AppCompatCheckBox)view;
    cb.setChecked((boolean)o);
    cb.setOnCheckedChangeListener(listener);
}

notifyDataSetChanged API 的行為應該會保存 item 之前的資料,只對需要的部分作改變,所以第三個 item checkbox 狀態會從 check 到 uncheck,如果有註冊了 listener,則理應通知 listener

那解法就是在初始化 checkbox 時,先 remove 掉 listener 在設定 checkbox,然後才註冊 listener,這樣就避免了不必要的 onCheckedChange 呼叫

此外,還有一個額外需要注意的點是,之前提過,用 notifyDataSetChanged 來更新 ListView,ListView item 之前的狀態都會被保存,所以原本被勾選的 item 更新後沒有被勾選的話會有 checkbox 被取消的動畫效果

如果不想要這個效果的話,則不應該用 notifyDataSetChanged,可以改用 setAdapter 重新設定 adapter 給 ListView,這樣 ListView 就會將整個內容銷毀再更新,就不會有 checkbox 效果,當然這樣會有效能流失,可以感覺到 GUI 的更新速度稍微慢了一點

沒有留言:

張貼留言