32776
iOSアプリのandroid版を開発中の者です。android、javaとも初心者のため、おかしな表現やコードの書き方等ありましたらご指摘ください。

ListFragmentを使用して単一選択のリスト画面を実装しています。  
ネットで見つけたサンプルコード(ArrayAdapter,Stringの配列を使用したもの)では、リスト行のタップで正しくチェックマークが切り替わることを確認できたのですが、  
自身の色々拡張したコードでは、どの行もチェックされていない状態で画面が起動し、また、タップしてもチェックマークが表示されない、という状況です。  
setItemChecked()をアダプターをリストにセットした後およびタップイベントに入れてみましたが、現象は変わらずで、原因が分からずにおります。

自身のコードでは、  
リストビューにセクションヘッダーが必要な為、Adapterクラスをextendして使用しています。  
また、行を再利用するサンプルコードで使用されていたViewHolderクラスを、iOSでいうUITableViewCellの拡張クラスのような位置づけにして使用しています。  
ほとんどの画面でリストビューを使用(行のレイアウトは画面毎に異なり、1つのリスト内で異なる行レイアウトを使用する画面もあります。)している為、このような作りにしております。

以下にコードを載せておきます。  
タップした際と、初期表示のタイミングで正しくチェックマークを表示させるにはどのようにしたらよいでしょうか？  
お手数ではありますが、問題箇所等ご指摘いただけますと大変助かります。  
どうぞよろしくお願いいたします。

```

    /** リストフラグメント */
    public class C010F_Options extends ListFragment {
    
        private MyAdapter mAdapter;
    
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
    
            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    
            //表示データ取得
            List<MyCellData> list = getCellData();
    
            //adapter生成
            mAdapter = new MyAdapter(getActivity(), R.layout.cell_011, list);
            setListAdapter(mAdapter);
            getListView().setItemChecked(0, true);
        }
    
        /// セルのタップイベント
        @Override public void onListItemClick(ListView l, View v, int position, long id) {
            l.setItemChecked(position, true);
            debug("押した。" + id + " " + position + " ");
        }
    
        /// 表示データをリストで取得
        private List<MyCellData> getCellData() {
            List<MyCellData> list = new ArrayList<>();
            //〜(省略)〜
            return list;
        }
    }
    
    
    /** アダプターに渡すデータのクラス */
    public class MyCellData {
    
        private Class cls;
        private int layoutID = 0; //レイアウトのID
        private int sectionNo = 0;
        private boolean isEnabled;
        private String key = "";
        private String itm = ""; //表示名
        private String rawVal = ""; //値(元)
        private String dispVal = ""; //値(表示用)
    
        public MyCellData(Class cls, int layoutID, int sectionNo, boolean isEnabled, String key, String itm) {
            this.cls = cls;
            this.layoutID = layoutID;
            this.sectionNo = sectionNo;
            this.isEnabled = isEnabled;
            this.key = key;
            this.itm = itm;
        }
    
        /// セッター
        //〜(省略)〜
    
        /// ゲッター
        //〜(省略)〜
    }
    
    
    /** Myアダプタークラス */
    public class MyAdapter extends ArrayAdapter<MyCellData> {
    
        public MyAdapter(Context context, List<MyCellData> objects) {
            super(context, 0, objects);
        }
    
        public MyAdapter(Context context, int resource, List<MyCellData> objects) {
            super(context, resource, objects);
        }
    
        @Override
        public @NonNull View getView(int position, View convertView, ViewGroup parent) {
            return getView(position, (B000_BaseCell)convertView, parent);
        }
    
        public @NonNull View getView(int position, B000_BaseCell convertView, ViewGroup parent) {
            B000_BaseCell cell = convertView;
            MyCellData data = getItem(position);
    
            //リユースできるかチェック
            boolean isReuseable = false;
            if (cell != null) {
                if (cell.getLayoutID() == data.getLayoutID()) {
                    isReuseable = true;
                }
            }
            //リユースできない場合、生成
            if (!isReuseable) {
                try {
                    //リフレクションでインスタンス生成
                    Class cls = data.getCls();
                    Class[] types = { Context.class };
                    Object[] args = { this.getContext() };
                    Constructor<B000_BaseCell> constructor = cls.getConstructor(types);
                    cell = constructor.newInstance(args);
    
                } catch(Exception e) {
                    fatal(e.getMessage()); //エラーメッセージ出力して終了
                }
    
            }
    
            //値をセット
            cell.setVal(data);
    
            return cell;
        }
    
        @Override
        public boolean isEnabled(int position) {
            return getItem(position).isEnabled();
        }
    }
    
    
    /** セル(リスト行)クラス */
    public class C011C_Option extends B000_BaseCell {
    
        public static final int LAYOUT_ID = R.layout.cell_011;
    
        public TextView lblItm;
    
        public C011C_Option(Context context) {
            super(context, LAYOUT_ID);
    
            View layout = LayoutInflater.from(context).inflate(LAYOUT_ID, this);
            lblItm = (CheckedTextView)layout.findViewById(android.R.id.text1);
        }
    
        @Override
        public void setVal(MyCellData data) {
            this.lblItm.setText(data.getItm());
        }
    }
    
    
    /** C011C_Optionクラスの親クラス */
    public class B000_BaseCell extends LinearLayout {
    
        private int layoutID = 0; //レイアウトのID
    
        private B000_BaseCell(Context context) {
            super(context);
        }
    
        public B000_BaseCell(Context context, int layoutID) {
            this(context);
            this.layoutID = layoutID;
        }
    
        public int getLayoutID() {
            return layoutID;
        }
    
        public void setVal(MyCellData data) {
        }
    }
    
    
    /** レイアウト　R.layout.cell_011 (android.R.layout.simple_list_item_single_choiceをコピペしたもの) */
    <?xml version="1.0" encoding="utf-8"?>
    <CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
                     android:id="@android:id/text1"
                     android:layout_width="match_parent"
                     android:layout_height="?android:attr/listPreferredItemHeightSmall"
                     android:textAppearance="?android:attr/textAppearanceListItemSmall"
                     android:gravity="center_vertical"
                     android:checkMark="?android:attr/listChoiceIndicatorSingle"
                     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
                     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />
    
```
CC BY-SA 3.0
2017-02-18T12:22:51.037
32774
2017-02-18T13:40:11.017
question
android java
SingleChoiceモードのListFragmentをタップしてもチェックマークが表示されない
<https://github.com/android/platform_frameworks_base/blob/82834baa358f55acb542e17da828b2d497cf8332/core/java/android/widget/AbsListView.java#L1184>

ListViewの実装を追いかけてみると、上のリンクのような記述が見つけられました。  
`ListView.CHOICE_MODE_SINGLE`の際に、ListViewの各要素のViewがCheckableであればそのViewに対してチェックをする、という実装でした。  
つまり、Adapterクラスの`getView()`メソッドで返すViewがCheckableであることが、`ListView.CHOICE_MODE_SINGLE`を使う条件だと読み取れます。

今回の場合だと、`C011C_Option`クラスがCheckableでないために、チェックを付けられなかったのです。（LinearLayoutはCheckableではない。）  
解決策として、

  * `C011C_Option` クラスにCheckableインタフェースを実装する
  * `lblItm` の型を `CheckedTextView` に変更
  * Checkableの各メソッドには、`lblItm`の対応するメソッドを呼び出すようにする

という形にすれば質問内容に関しては解決するのではないでしょうか。

```

    public class C011C_Option extends B000_BaseCell implements Checkable {
    
        public static final int LAYOUT_ID = R.layout.cell_011;
    
        public CheckedTextView lblItm;
    
        public C011C_Option(Context context) {
            super(context, LAYOUT_ID);
    
            View layout = LayoutInflater.from(context).inflate(LAYOUT_ID, this);
            lblItm = (CheckedTextView)layout.findViewById(android.R.id.text1);
        }
    
        @Override
        public void setVal(MyCellData data) {
            this.lblItm.setText(data.getItm());
        }
    
        @Override
        public void setChecked(boolean checked) {
            this.lblItm.setChecked(checked);
        }
    
        @Override
        public boolean isChecked() {
            return this.lblItm.isChecked();
        }
    
        @Override
        public void toggle() {
            this.lblItm.toggle();
        }
    }
    
```

参考までに、手元で動作確認が取れたサンプルコードを添付しておきます。  
<https://github.com/litmon/SingleChoiceListViewSample/tree/master/app/src/main/java/com/litmon/app/singlechoicelistviewsample>
CC BY-SA 3.0
2017-02-18T13:40:11.017
32776
2017-02-18T13:40:11.017
7655
32774
answer