2か月ほど詰まっていたので備忘録
実行環境
IDE:Android Studio 3.4
言語:Java
Xperia XZ3(実機デバッグに利用)
OSはAndroid 9 (API Level 28)
何がしたいか
ListView内のCheckBoxがスクロールによってそのチェック状態を変更されることがないようにする。
解決方法
ListViewで表示する要素を使いまわす処理をするgetViewメソッド内で、ListViewのCheckBoxのリスナーの貼り直しと、onCheckedChangedメソッド内でgetViewの引数のpositionを利用して改めてitemを取得する。
以下のコードには表記していませんが
ArrayAdapter< ListViewに表示するクラス型 >をextends(継承)、
ViewHolderパターンを使っています。
以下が成功例となります。
// ListItemはListViewで表示する要素の自作クラス。
private ListItem item;
// 省略 ~~~~~~~~~~~~
public View getView (final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
// convertViewに何もなければ新しく生成する。
convertView = mInflater.inflate(mResId, parent, false);
// リスト内の各アイテムの要素を取得。
holder = new ViewHolder();
holder.isActive = convertView.findViewById(R.id.alarm_active);
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
// リストビューに表示するアイテムのインスタンスを取得する。
item = getItem(position);
// リスナーを剥がす。
holder.isActive.setOnCheckedChangeListener(null);
// 要素にpositionから取得したitem(自作クラスのインスタンス)を要素へ代入。
holder.isActive.setChecked(item.isActive());
// リスナーを貼る。
holder.isActive.setOnCheckedChangeListener(
new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged (CompoundButton cb, boolean isChecked) {
ListItem listItem = getItem(position);
// 対warning用nullチェック
if (listItem != null){
listItem.setActive(isChecked);
}
}
});
return convertView;
}
ここで重要なのは匿名クラス内のonCheckedChangedメソッド内の
ListItem listItem = getItem(position);
という処理です。
この処理はチェック状態をリストの要素(自作クラス)に反映するためのものですが、
すでにitem = getItem(position);
のようにされているからといってこの部分を削除し、
item.setActive(isChecked);
のようにしてしまうと正常に動作しません。