Android Studioのチェック付きListViewでスクロールするとチェックの状態が変わってしまう(解決)

#チェックの状態が変わってしまう

まず、スクロールするほどのチェックを追加したとして、それでスクロールしたらなぜかチェックの状態が変わってしまうのです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1061021/992eef40-871a-3485-de5a-55f9e2e9f789.png" width="310">
↑のように、アイテム１がチェックされていない状態でスタートして、
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1061021/51920a4b-4c0d-7d09-66b7-492e2a31b2de.png" width="310">
↑のように、アイテム１にチェックを入れます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1061021/a8252aac-62a8-af0d-b06f-ab877d540cc2.png" width="310">
↑のように、下にスクロールします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1061021/502593e1-8b16-be20-9de2-2549f2ace5bf.png" width="310">
すると、なんと↑のように、チェックの状態が変わってしまうのです。（アイテム１が。）

#変わってしまう理由

まず、画面外になってしまった場合はその項目は廃棄されます。(再利用される)
独自adapterのコードではgetView(...)というところがありますよね。
それは各行が読み込まれたときに呼ばれる関数なんです。
廃棄されるので、そのgetViewのところでもっかい反映させないといけないんです。

#チェックリストのコードは大きく分けて二つの種類

・SimpleAdapter
・ArrayAdapter<listViewData>

(listViewDataは独自のclassの名前)

##SimpleAdapter

これは主にMapを使う方法です。Mapに"check"と"name"を作成して、それをputする感じです。
<font color="red">しかし、この方法だとダメでした。</font>
だめというか、自分の知能だと解決できませんでした。getItem(position)などから
チェックの状態を反映させてもだめでした。

##ArrayAdapter

この方法を使って解決しました。
これは独自のclassからsetTextDataとかをしたりsetCheckedをしたりgetTextDataからgetViewのところでセットしたり、
<u>getCheckedからgetViewのところでセットしたり</u>とする感じです。
なかなかこの説明では理解しずらいと思いますが、まぁこの方法を使えば解決できるということです。

#どこをどのように変更したのか

まず、SimpleAdapterを使うのは無理だと分かったので、ArrayAdapterを継承したやつにしまた。

そして独自AdapterのgetViewのところをこうしました。

```java:MyAdapter.java
    public View getView(int position, View convertView, ViewGroup parent) {
        // 特定行(position)のデータを得る
        MyData item = (MyData)getItem(position);
        // convertViewは使いまわされている可能性があるのでnullの時だけ新しく作る
        if (null == convertView) convertView = mLayoutInflater.inflate(R.layout.list, null);

        // MyDataのデータをViewの各Widgetにセットする
        TextView textView = convertView.findViewById(R.id.textView);
        textView.setText(item.getTextData());
        CheckBox checkBox = convertView.findViewById(R.id.checkBox);
        checkBox.setOnCheckedChangeListener(null);
        checkBox.setChecked(item.isChecked());
        checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                MyData MyData = getItem(position);
                MyData.setChecked(b);
            }
        });

        return convertView;
    }
```

まず最初に注目してほしいのがここです。

```java
        // MyDataのデータをViewの各Widgetにセットする
        TextView textView = convertView.findViewById(R.id.textView);
        textView.setText(item.getTextData());
        CheckBox checkBox = convertView.findViewById(R.id.checkBox);
        checkBox.setOnCheckedChangeListener(null);
        checkBox.setChecked(item.isChecked());
```
この行がないと、なにもないチェックの状態もfalseでtextViewのデータも空の状態になってしまいます。
checkBoxは念のため、リスナーを外してから、反映しています。

そしてからcheckBoxにチェンジリスナ―を付けました。

また、

```java
        checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                MyData MyData = getItem(position);
                MyData.setChecked(b);
            }
        });
```

というふうに、チェンジリスナーをつけておかないと、ずっとチェックがfalseになってしまうため、スクロールして画面外になると勝手にチェックが外されるといった挙動になります。

実際に作ってみました↓
<a href="https://programming-self-study.blogspot.com/2021/08/android-studio-listview-checkbox.html">Android Studio の ListView に CheckBox を追加する | チェックリストを作成</a>

参考：
<a href="https://qiita.com/Ur_ys_kn_k_/items/212a03ad0eef08e0bfad">[Android][Java] ListViewのCheckBoxの状態を管理する</a>