読者です 読者をやめる 読者になる 読者になる

ほげほげ(仮)

仮死状態

AlertDialog.Builder#setItemsの項目管理を改善する

Android

AlertDialog.Builder#setItemsはリスト形式のダイアログを表示してくれますが、どうしても項目の管理がやっかいでした。
色々試して項目管理を楽できるようにしてみました。

問題点

AlertDialog.Builder#setItemsは第一引数に配列リソースIDのとString配列の2つのメソッドがあります。
これ自体は特に問題ないのですが、問題は第二引数のDialogInterface.OnClickListenerほうです。

DialogInterface.OnClickListener#onClickは第二引数に選択されたリストのindexが渡されてきます。
このindexはAlertDialog.Builder#setItemsの第一引数の配列に依存します。

ということは配列の順番を変えたらonClickのindexでの判定も変更しなければいけません。
第一引数に配列リソースIDを指定した場合はXMLとコードを変更する必要があります。

これが非常に気に入らないところです。
リストの順番を変えるのは一箇所のみで修正したいとこです。

// ここの順番を変えるとonClick内の判定も変更する必要がある
String[] items = new String[]{"リスト1", "リスト2"};

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setItems(getOrderItems(), new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        // 項目の順番が変わったらココも修正する必要がある
        switch (which) {
        case 0:
            // リスト1が押された時の処理
            break;
        case 1:
            // リスト2が押された時の処理
            break;
        }
    }
});
builder.create().show();

解決策

色々試して自分の中でしっくりできるやり方を見つけました。

まずコード上でString配列を渡すのではなく配列リソースIDを渡す方を使います。
で、リストに表示する文字列もリソースを使います。

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="list1">リスト1</string>
  <string name="list2">リスト2</string>
  <string name="list3">リスト3</string>
</resources>

arrays.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string-array name="list_dialog">
    <item>@string/list1</item>
    <item>@string/list2</item>
    <item>@string/list3</item>
  </string-array>
</resources>


次にAlertDialog.Builderを拡張します。
内容としては新たにListenerを作成して、setItemsもオーバーロードします。
DialogInterface.OnClickListener内から変換してarrays.xmlに指定したstrings.xmlのIDを渡すようにしています。

public class ListDialogBuilder extends AlertDialog.Builder {
 
    private final Context mContext;
 
    public ListDialogBuilder(Context context) {
        super(context);
        mContext = context;
    }
 
    @SuppressLint("NewApi")
    public ListDialogBuilder(Context context, int theme) {
        super(context, theme);
        mContext = context;
    }
 
    public void setItems(final int itemsId, final OnItemClickListener listener) {
        setItems(itemsId, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                TypedArray items = mContext.getResources().obtainTypedArray(itemsId);
                int resId = items.getResourceId(which, 0);
                listener.onClick(dialog, resId);
            }
 
        });
    }
 
    public interface OnItemClickListener {
        void onClick(DialogInterface dialog, int resId);
    }
}

あとはこれを呼び出すようにするだけです。
どの項目が選択されたかはstrings.xmlに指定されたIDで判定する感じになります。

ListDialogBuilder builder = new ListDialogBuilder(this);
builder.setItems(R.array.list_dialog, new ListDialogBuilder.OnItemClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int resId) {
        switch (resId) {
        case R.string.list1:
            // リスト1の処理
            break;
        case R.string.list2:
            // リスト2の処理
            break;
        case R.string.list3:
            // リスト3の処理
            break;
        }
    }
});
builder.create().show();

これを使用すると項目の順番を変える時はarrays.xmlのみを修正すればOKになります。

今回使用したコードはGistにあげてあります。
【Android】リストダイアログの項目管理を楽にする

まとめ

ここまでする必要あるかは微妙ですが、ずっと気持ち悪い感じだったので…