2012/10/19

CheckedTextView と SimpleAdapter の組み合わせで IllegalStateException

以下のようなコードを Android 2.2 以前の端末で実行すると IllegalStateException が発生します。
List<Map<String, String>> data = new ArrayList<Map<String, String>>();
for (int i = 0; i < 10; i++) {
    Map<String, String> map = new HashMap<String, String>();
    map.put("item", String.valueOf(i));
    data.add(map);
}
SimpleAdapter adapter = new SimpleAdapter(
                this,
                data,
                android.R.layout.simple_list_item_single_choice,
                new String[] {"item"},
                new int[] {android.R.id.text1});
mListView.setAdapter(adapter);

LogCat の出力は以下のような感じ
E/AndroidRuntime(2226): java.lang.IllegalStateException: android.widget.CheckedTextView should be bound to a Boolean, not a class java.lang.String

これ、やっかいなのは Android 2.3 以降では普通に実行できるのです。
なので、きちんと古い端末で動作確認しないと問題を発見できません。

この問題の原因は SimpleAdapter の実装にあります。
2.3 以降の SimpleAdapter は TextView を継承している View に対して暗黙的にテキスト (データに対しtoString()したもの) を割り当ててくれるのですが、古いものにはそのような機能はありません。

そのため、2.2 以前を対象にする場合は ViewBinder を明示的に定義してやる必要があります。
具体的には、以下のように setAdapter より手前で setViewBinder を呼んでやります。
adapter.setViewBinder(new ViewBinder() {
    @Override
    public boolean setViewValue(View view, Object data, String textRepresentation) {
        CheckedTextView v = (CheckedTextView) view;
        v.setText((CharSequence) data);
        return true;
    }
});
mListView.setAdapter(adapter);

0 件のコメント: