Androidでリストビュー（ListView）をカスタムして表示する

今回のエントリでは、Androidで以下のように画像サムネイルなどを表示したListViewを実装する方法を紹介していきます。
ListViewの要素をカスタマイズするには、ArrayAdapterを継承したクラスを実装することで実現することができます。

![listview_sample1.png](https://qiita-image-store.s3.amazonaws.com/0/69983/08e5c0d3-6fb9-3f93-debd-fe4a154472d6.png)


## 1. レイアウトファイルを追加
まず、レイアウトファイルにリストビューの要素とリストビューに表示させたい要素を記入します。
以下の例では、**activity_main.xml**にリストビューの要素、**samplelist_item.xml**にリストビュー内にサムネイル画像とタイトルを表示するようにしています。

```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="org.example.customlistviewsample.MainActivity">

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

</RelativeLayout>
```

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

    <ImageView
        android:id="@+id/thumbnail"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:padding="3dp" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="70dp"
        android:textColor="#000"
        android:padding="5dp"
        android:gravity="center_vertical"
        android:maxLines="1"
        android:text="Sample Text"/>

</LinearLayout>
```

## 2. 要素を格納するクラスを実装
次にArrayAdapterを継承してリストビューに表示する要素をカスタマイズするために必要となる、カスタマイズする要素を格納するクラスを実装します。
以下**SampleListItem.java**は、サムネイル画像とタイトルを格納するクラスです。

```java:SampleListItem.java
package org.example.customlistviewsample;

import android.graphics.Bitmap;

public class SampleListItem {
    private Bitmap mThumbnail = null;
    private String mTitle = null;

    /**
     * 空のコンストラクタ
     */
    public SampleListItem() {};

    /**
     * コンストラクタ
     * @param thumbnail サムネイル画像
     * @param title タイトル
     */
    public SampleListItem(Bitmap thumbnail, String title) {
        mThumbnail = thumbnail;
        mTitle = title;
    }

    /**
     * サムネイル画像を設定
     * @param thumbnail サムネイル画像
     */
    public void setThumbnail(Bitmap thumbnail) {
        mThumbnail = thumbnail;
    }

    /**
     * タイトルを設定
     * @param title タイトル
     */
    public void setmTitle(String title) {
        mTitle = title;
    }

    /**
     * サムネイル画像を取得
     * @return サムネイル画像
     */
    public Bitmap getThumbnail() {
        return mThumbnail;
    }

    /**
     * タイトルを取得
     * @return タイトル
     */
    public String getTitle() {
        return mTitle;
    }
}
```

## 3. ArrayAdapterを継承したクラスを実装
続いて、上記2で実装した**SampleListItem**クラスを利用して、ArrayAdapterを継承したクラスを実装します。

```java:SampleListAdapter.java
package org.example.customlistviewsample;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;

public class SampleListAdapter extends ArrayAdapter<SampleListItem> {

    private int mResource;
    private List<SampleListItem> mItems;
    private LayoutInflater mInflater;

    /**
     * コンストラクタ
     * @param context コンテキスト
     * @param resource リソースID
     * @param items リストビューの要素
     */
    public SampleListAdapter(Context context, int resource, List<SampleListItem> items) {
        super(context, resource, items);

        mResource = resource;
        mItems = items;
        mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view;

        if (convertView != null) {
            view = convertView;
        }
        else {
            view = mInflater.inflate(mResource, null);
        }

        // リストビューに表示する要素を取得
        SampleListItem item = mItems.get(position);

        // サムネイル画像を設定
        ImageView thumbnail = (ImageView)view.findViewById(R.id.thumbnail);
        thumbnail.setImageBitmap(item.getThumbnail());

        // タイトルを設定
        TextView title = (TextView)view.findViewById(R.id.title);
        title.setText(item.getTitle());

        return view;
    }
}
```

## 4. リストビューに出力
最後に上記の内容を用いて以下のように、リストビューに要素を出力していきます。
簡単に説明すると、**setAdapter**メソッドの入力値として、上記3で実装したSimpleArrayAdapterクラスを設定することで、カスタマイズされたリストビューを出力することができます。
以下の例では、サムネイル画像の箇所にデフォルトのAndroidのアプリアイコン、タイトルに0から2までをループさせた値が含まれているテキストを表示するようなものになっています。

```java:MainActivity.java
package org.example.customlistviewsample;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    /**
     * アクティビティ生成時に呼ばれる
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // レイアウトからリストビューを取得
        ListView listView = (ListView)findViewById(R.id.sample_listview);

        // リストビューに表示する要素を設定
        ArrayList<SampleListItem> listItems = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);  // 今回はサンプルなのでデフォルトのAndroid Iconを利用
            SampleListItem item = new SampleListItem(bmp, "sample text No. " + String.valueOf(i));
            listItems.add(item);
        }

        // 出力結果をリストビューに表示
        SampleListAdapter adapter = new SampleListAdapter(this, R.layout.samplelist_item, listItems);
        listView.setAdapter(adapter);
    }

}
```

![listview_sample2.png](https://qiita-image-store.s3.amazonaws.com/0/69983/bc87c685-7c80-e680-e741-fbae1fce9150.png)

## 補足
カスタムして実装したListViewの要素をタップ後のイベントで利用したいというときは、以下のようにgetItemAtPositionメソッドで取得した要素を上記2で作成した、SampleListItemクラスにキャストして取得します。
MainActivity.javaに以下の内容を追加するとタップした要素のIDとテキストがダイヤログで表示されます。
※下記、サンプルソースコードには既に追記しています。

```java:MainActivity.java追記
.
.
.
import android.support.v7.app.AlertDialog;  // ダイヤログ表示用
.
.
.

    /**
     * アクティビティ生成時に呼ばれる
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        .
        .
        .
        listView.setOnItemClickListener(onItemClickListener);  // タップ時のイベントを追加
    }


    /**
     * リストビューのタップイベント
     */
    private AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            // タップしたアイテムの取得
            ListView listView = (ListView)parent;
            SampleListItem item = (SampleListItem)listView.getItemAtPosition(position);  // SampleListItemにキャスト

            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setTitle("Tap No. " + String.valueOf(position));
            builder.setMessage(item.getTitle());
            builder.show();
        }
    };
```

![listview_sample3.png](https://qiita-image-store.s3.amazonaws.com/0/69983/1a1a2dd8-a06b-dad2-0969-63f5c3310336.png)


## サンプルソースコード
上記の内容を以下Githubにアップロードしているので、興味のある方は実際に利用して確認してみてください。
https://github.com/ksugawara61/CustomListViewSample

## 参考文献
1. ListViewの行にカスタムレイアウトを使う方法 - Qiita, http://qiita.com/mizofumi0411/items/fd51dea947f2e65f534b, Online; accessed 16-October-2016.
2. リストビューをカスタマイズする _ TechBooster, http://techbooster.org/android/ui/1282/, Online; accessed 16-October-2016.
3. ListViewに画像サムネイル付きテキストを表示してみる - Androidアプリ開発入門 -ANDROID ROID-Androidアプリ開発入門 -ANDROID ROID-, http://androidroid.info/android/listview/45/, Online; accessed 16-October-2016.
4. AndroidアプリでListViewをカスタマイズし，Web上の画像を行ごとに表示するサンプルコード （SimpleAdapterクラスを独自に拡張） - 主に言語とシステム開発に関して, http://language-and-engineering.hatenablog.jp/entry/20111014/p1, Online; accessed 16-October-2016.