カスタムListViewの作成手順

Androidアプリの一覧表示などでよく使うListViewの使い方メモです。
単純に文字だけのリストだと楽ですが、自分で表示する項目やレイアウトを調整する場合はいろいろ設定がいったのでまとめます。

# Viewを作る
ListView本体と、ListView内に表示する項目をまとめたレイアウトを用意します。
![image](https://qiita-image-store.s3.amazonaws.com/0/83530/e6fd7262-6e74-d128-9ea9-62ee967f629d.png)
イメージとしてはこんな感じ。
左がListViewを貼り付けた段階。右が自分でカスタムしたレイアウトです。
ListView内の項目一つ一つが右のレイアウトに置き換えるイメージです。

# エンティティ作成
自分で作成したレイアウト内で必要な項目を保持するエンティティクラスを作ります。

```java
public class Item {
    private long id;
    private String title;

    public Item(String title){
        id = Math.abs(UUID.randomUUID().hashCode());
        this.title = title;
    }

    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
}
```
UUID生成でidを作っていますが、必須項目ではないです。
ListViewはListなので、このエンティティのListを作っておいて、ListViewに紐付けます。

# カスタムAdapterの作成
ListViewとデータを紐付けるための仕組みにAdapterというのがあります。
今回は表示項目をカスタマイズしているので、上記で作ったエンティティのデータをどの項目に当てはめるか、というのを設定してやる必要があります。
また、Adapterを設定しておくとエンティティのListにデータを更新するとそのデータに応じてちゃんとListViewも反映してくれます。

```java
public class ItemAdapter extends BaseAdapter {
    private LayoutInflater layoutInflater;
    private ArrayList<Item> itemList;

    public ItemAdapter(Context context){
        this.layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        itemList = new ArrayList<Item>();
    }

    public void add(String title){
        this.itemList.add(new Item(title));
    }
    public void add(Item item){
        this.itemList.add(item);
    }

    @Override
    public int getCount() {
        return itemList.size();
    }

    @Override
    public Object getItem(int position) {
        return itemList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return itemList.get(position).getId();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        convertView = layoutInflater.inflate(R.layout.sub, parent, false);

        ((TextView)convertView.findViewById(R.id.subTitle))
                .setText(itemList.get(position).getTitle()+" id:"+itemList.get(position).getId());
        return convertView;
    }
}
```
BaseAdapterを継承して作成します。
エンティティのリスト（ここではitemList）を作成しておき、リストへの追加や削除など必要な機能の実装とgetCount()/getItem()などのオーバーライドが必要なメソッドの実装を行います。
また、LayoutInflaterを作成しておきます。

# LayoutInflaterによるView作成

Adapterでの実装が必須なメソッドにgetView()があります。
データが追加された時にこのメソッドが実行され、ListView内の行が描画されます。
ここで必要なのが、今回準備したカスタムレイアウトを読み込むこととカスタムレイアウトの項目にデータをセットすることです。

```java
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    //①
    convertView = layoutInflater.inflate(R.layout.sub, parent, false);
    //②
    ((TextView)convertView.findViewById(R.id.subTitle))
            .setText(itemList.get(position).getTitle()+"id:"+itemList.get(position).getId());
    return convertView;
}
```
1. カスタムレイアウトの読み込みにLayoutInflaterを利用します。
これでレイアウトファイルをViewとして読み込むことができます。
2. 作成したViewに対してItemList内から取り出したその行に対応するエンティティのデータをセットします。

最後のViewを返します。

# ListViewとAdapterの設定

最後にListViewに対して先ほど作成したAdapterの設定を行います。

```java
 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    itemAdapter = new ItemAdapter(this);
    ListView listView = (ListView)findViewById(R.id.listView);
    listView.setAdapter(itemAdapter);
    
    //クリック時の動作
    ((Button)findViewById(R.id.btnAdd)).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            itemAdapter.add("追加");
            itemAdapter.notifyDataSetChanged();
        }
    });
}
```
動作確認としてボタン押したらadapterにデータ追加をしてみています。
notifyDataSetChanged()で更新されたことを通知しています。これでListViewの描画も更新されます。

![image](https://qiita-image-store.s3.amazonaws.com/0/83530/55ccdaa0-d41d-0236-ecbb-8fd359f09356.png)

こんな感じになりました。