[Android][Java]ボタン付きのカスタムセルのListViewを作成する

ボタン付のカスタムしたセルでListViewを作成する方法を記載します。
リスト表示する場合にはRecyclerViewの方が柔軟に様々な事ができますが、その分実装量が多くなるため、簡単に実装ができるListViewについて書いていきます。
完成イメージは以下です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545703/0d98c3c7-21cc-7894-906a-0e6043f11016.png" width=50%/>
AndroidStudioで空のアクティビティを作成した状態から始めます。
#１．layout/activity_main.xmlにListViewを定義する
空のアクティビティを作成すると、ConstraintLayoutだけが定義されている状態なので、以下のようにListViewを定義します。

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/mainlist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</android.support.constraint.ConstraintLayout>
```

#２．カスタムセルとなるlayout/row.xmlを追加
layoutフォルダ配下に、row.xmlファイルを追加します。これは、ListViewの1行ずつとなるカスタムセルのレイアウトファイルとなり、中身を以下のように定義します。

```row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
    android:weightSum="1">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/text1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:layout_weight="1"
            android:text="Text1"
            android:textSize="18dp" />

        <TextView
            android:id="@+id/text2"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="Text2"
            android:gravity="center_vertical|right"
            android:textSize="18dp" />

        <Button
            android:id="@+id/rowbutton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:focusable="false"
            android:focusableInTouchMode="false"
            android:text="BTN" />

    </LinearLayout>
</LinearLayout>
```

#３．カスタムセルをListViewにバインドするためのアダプタクラスを作成
作成したrow.xmlをListViewにバインドするためには、アダプタクラスを利用します。アダプタクラスには、SimpleAdapterクラスがSDKとして用意されており、今回はカスタムセルをバインドするため、継承したListViewAdapterクラスを作成します。

```ListViewAdapter.java
public class ListViewAdapter extends SimpleAdapter {

    private LayoutInflater inflater;
    private List<? extends Map<String, ?>> listData;

    // 各行が保持するデータ保持クラス
    public class ViewHolder {
        TextView text1;
        TextView text2;
    }

    public ListViewAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
        super(context, data, resource, from, to);
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.listData = data;
    }
}
```

#４．SimpleViewAdapterのgetViewメソッドをOverrideする
row.xmlで用意したTextViewやボタンの実装を行うためにListViewAdapterの中でgetViewメソッドをOverrideします。アプリ起動時やセル選択時に呼ばれます。

```ListViewAdapter.java
public class ListViewAdapter extends SimpleAdapter {

    private LayoutInflater inflater;
    private List<? extends Map<String, ?>> listData;

    // 各行が保持するデータ保持クラス
    public class ViewHolder {
        TextView text1;
        TextView text2;
    }

    public ListViewAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
        super(context, data, resource, from, to);
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.listData = data;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final ViewHolder holder;

        // ビューを受け取る
        View view = convertView;

        if (view == null) {
            // 画面起動時にViewHolderを作成する
            view = inflater.inflate(R.layout.row, parent, false);

            holder = new ViewHolder();
            holder.text1 = (TextView) view.findViewById(android.R.id.text1);
            holder.text2 = (TextView) view.findViewById(android.R.id.text2);

            view.setTag(holder);
        } else {
            // 行選択時などは既に作成されているものを取得する
            holder = (ViewHolder) view.getTag();
        }

        String text1 = ((HashMap<?, ?>) listData.get(position)).get("text1").toString();
        String text2 = ((HashMap<?, ?>) listData.get(position)).get("text2").toString();
        holder.text1.setText(text1);
        holder.text2.setText(text2);

        // セル上にあるボタンの処理
        Button btn = (Button) view.findViewById(R.id.rowbutton);
        btn.setTag(position);
        btn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                // 選択したセルの文字を赤色にする
                holder.text1.setTextColor(Color.RED);
            }
        });

        return view;
    }
}
```

#５．メイン画面のクラスでListViewに表示するデータをセットする
MainActivityから、４．で作成したListViewAdapterクラスにデータを渡し、そのアダプターをListViewにバインドする事で表示する事ができます。

```MainActivity.java
public class MainActivity extends AppCompatActivity {
    public static Map<String, String> data;
    public static List<Map<String, String>> dataList;
    public static ListView listView;
    public static ListViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dataList = new ArrayList<Map<String, String>>();

        // ListViewに表示するためのDATAを作成する
        int MAXDATA = 10;
        for (int i = 0; i < MAXDATA; i++) {
            data = new HashMap<String, String>();
            data.put("text1", "タイトル" + i);
            data.put("text2", "サブ" + i);
            dataList.add(data);
        }

        // アダプターにデータを渡す
        adapter = new ListViewAdapter(
                this,
                dataList,
                R.layout.row,
                new String[] { "text1", "text2" },
                new int[] { android.R.id.text1,
                        android.R.id.text2 });

        // ListViewにアダプターをSETする
        listView = (ListView) findViewById(R.id.mainlist);
        listView.setAdapter(adapter);
        listView.setTextFilterEnabled(false);
    }
}
```

以上で、カスタムセルをListViewに表示することができます。
#ソースコードのリポジトリ
上記のソースコードとそのプロジェクトはGitHubにて公開していますので合わせて確認してみて下さい。
[https://github.com/highcom/ListViewTest](https://github.com/highcom/ListViewTest)

#おわりに
以前、個人ブログでまとめていたものを改めて整理してQiitaにまとめさせてもらいました。
少しでもこの内容を読む人が増えれば良いなと思います。
いつも、Qiitaの投稿記事にはお世話になっているので、これからは自分も少しずつ記事を書いて貢献していければと思います。