いろいろガンガンいこうぜ。体も大事に

普段はJavaでAndroidアプリ開発しているプログラマーのブログです。

ListAdapterについて調べてみた(2) SimpleAdapter編その1

前回ArrayAdapterを使ってみました。
ArrayAdapterは,一つのTextViewだけセルごとに値を設定する場合,getViewメソッドをオーバーライドしたりせず非常に少ないコード量で書けます。
しかし,TextViewではなくImageViewの要素をセルごとに設定したり,セルごとに複数のTextViewに値を設定したり,画像と文字列両方セルごとに設定したりということは,getViewメソッドのオーバーライドメソッドなしには実現できません。


このようなことはSimpleAdapterを使えば,比較的容易にそしてgetViewメソッドのオーバーライドなしに少ないコード量で書けます。


具体例と一緒に使い方を見ていきます。


やりたいことと設定

こんなクラスがあるとします。

public class ColorItem {
	private final String nameEnglish;
	private final String nameJapanese;
	private final int imageId;

	public ColorItem(String nameEnglish, String nameJapanese, int imageId) {
		this.nameEnglish = nameEnglish;
		this.nameJapanese = nameJapanese;
		this.imageId = imageId;
	}

	public String getNameEnglish() {
		return nameEnglish;
	}

	public String getNameJapanese() {
		return nameJapanese;
	}

	public int getImageId() {
		return imageId;
	}
}

こんなレイアウトXMLファイルがあるとします。
res/layout/list_item_for_simple_adapter.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="70dp" >

    <TextView
        android:id="@+id/name_english_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="15dp"
        android:text="英語名" >
    </TextView>

    <TextView
        android:id="@+id/name_japanese_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_marginBottom="15dp"
        android:layout_marginLeft="20dp"
        android:text="日本語名" >
    </TextView>

    <ImageView
        android:id="@+id/color_image_view"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:layout_margin="10dp" />

</RelativeLayout>

ColorItemのListのインスタンスがあり,そのインスタンスの情報をListViewに表示したいです。
list_item_for_simple_adapterをセルのレイアウトとし,ColorItemクラスのインスタンスの情報をセットするためのSimpleAdapterインスタンスを作成します。


ColorItemのnameEnglishの文字列の値を上記レイアウトXMLファイル中のidがname_english_text_viewのTextView中にセットし,nameJapaneseの文字列をidがname_japanese_text_viewのTextView中にセットし,imageIdの値をidとする画像リソースをcolor_image_viewというidのImageViewにセットします。



サンプルコード
public class SimpleAdapterSampleActivity extends ListActivity {
    private static final String KEY_NAME_ENGLISH = "name_english";
    private static final String KEY_NAME_JAPANESE = "name_japanese";
    private static final String KEY_IMAGE_ID = "image_id";

    private static final String[] FROM = new String[] {
            KEY_NAME_ENGLISH, KEY_NAME_JAPANESE, KEY_IMAGE_ID
    };
    private static final int[] TO = new int[] {
            R.id.name_english_text_view, R.id.name_japanese_text_view, R.id.color_image_view
    };

    private static final List<ColorItem> COLOR_ITEM;

    static {
        COLOR_ITEM = new ArrayList<ColorItem>();
        COLOR_ITEM.add(new ColorItem("Blue", "青", R.drawable.blue));
        COLOR_ITEM.add(new ColorItem("Cyan", "シアン", R.drawable.cyan));
        COLOR_ITEM.add(new ColorItem("Green", "緑", R.drawable.green));
        COLOR_ITEM.add(new ColorItem("Magenta", "マゼンタ", R.drawable.magenta));
        COLOR_ITEM.add(new ColorItem("Red", "赤", R.drawable.red));
        COLOR_ITEM.add(new ColorItem("Yellow", "黄色", R.drawable.yellow));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        List<ColorItem> colorItemList = new ArrayList<ColorItem>(COLOR_ITEM);

        List<Map<String, ?>> data = colorItemListToMapList(colorItemList);
        int layoutResourceId = R.layout.list_item_for_simple_adapter;

        SimpleAdapter adapter = new SimpleAdapter(this, data, layoutResourceId, FROM, TO);

        setListAdapter(adapter);
    }

    private List<Map<String, ?>> colorItemListToMapList(List<ColorItem> colorItemList) {
        List<Map<String, ?>> data = new ArrayList<Map<String, ?>>();
        for (ColorItem colorItem : colorItemList) {
            data.add(colorItemToMap(colorItem));
        }
        return data;
    }

    private Map<String, ?> colorItemToMap(ColorItem colorItem) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(KEY_NAME_ENGLISH, colorItem.getNameEnglish());
        map.put(KEY_NAME_JAPANESE, colorItem.getNameJapanese());
        map.put(KEY_IMAGE_ID, Integer.valueOf(colorItem.getImageId()));

        return map;
    }
}

次の節から詳しく中身を見ていきます

SimpleAdapterのコンストラクタ

SimpleAdapterコンストラクタは一つで

public SimpleAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)

のみとなっています。

それぞれの引数の意味を簡単に説明すると,
第2引数は,キーがString型のMapのList(詳しくは後述)
第3引数は,セルのレイアウトXMLファイルのid
第4引数は,第2引数のMapのキーの配列
第5引数は,第3引数のレイアウトXMLファイル内のビューのidの配列

です。

まずColorItemの各フィールドの値とビューのidを紐付ける仲介を行うマップのキーを定義します。

static final String KEY_NAME_ENGLISH = "name_english"; // 英語名のデータとビューのためのキー
static final String KEY_NAME_JAPANESE = "name_japanese"; // 日本語名のデータとビューのためのキー
static final String KEY_IMAGE_ID = "image_id"; // 画像のデータとビューのためのキー

このキーを使って,SimpleAdapterの第4引数を作ります。

static final String[] FROM = new String[] {
    KEY_NAME_ENGLISH,
    KEY_NAME_JAPANESE,
    KEY_IMAGE_ID
};

次はint[]型の第5引数です。
第4引数の配列は,英語名,日本語名,画像idの順番でキーが並んでいます。
その順番で,それぞれのキーと対応するビューのidを要素とするint型の配列を作ります。それそれのビューのidは,第3引数のレイアウト中のビューのidでないといけません。

static final int[] TO = new int[] { 
    R.id.name_english_text_view, 
    R.id.name_japanese_text_view, 
    R.id.color_image_view 
};

最後に第2引数です。これはキーがString型のMapのListになっています。
SimpleAdapterを使うためには表示したいデータをこの型に変換する必要があります。


コンストラクタの第2引数 キーがString型のMapのリスト

コンストラクタの第2引数はキーがString型のMapのListです。
まずColorItemのインスタンスを,キーがString型のMapに変換するメソッドを作ります。

private Map<String, ?> colorItemToMap(ColorItem colorItem) {
	Map<String, Object> map = new HashMap<String, Object>();
	map.put(KEY_NAME_ENGLISH, colorItem.getNameEnglish());
	map.put(KEY_NAME_JAPANESE, colorItem.getNameJapanese());
	map.put(KEY_IMAGE_ID, Integer.valueOf(colorItem.getImageId()));

	return map;
}

キーは第4引数にも用いた文字列で,バリューはそのキーと対応するColorItem型のフィールドです。
このメソッドを使って,ColorItemのListを変換するメソッドを定義します。

private List<Map<String, ?>> colorItemListToMapList(List<ColorItem> colorItemList) {
	List<Map<String, ?>> data = new ArrayList<Map<String, ?>>();
	for (ColorItem colorItem : colorItemList) {
		data.add(colorItemToMap(colorItem));
	}
	return data;
}
SimpleAdapterのインスタンスの作成

ActivityのonCreate内で,

List<Map<String, ?>> data = colorItemListToMapList(colorItemList);
int layoutResourceId = R.layout.list_item_for_simple_adapter;

SimpleAdapter adapter = new SimpleAdapter(this, data, layoutResourceId, FROM, TO);

とすれば,前述のやりたいことを実現できるSimpleAdapterが作成できます。


まとめ

SimpleAdapterを使って,セルごとに複数のTextViewに文字列とImageViewに画像を設定することができました。