DialogFragmentであれこれ – Android

[PR]

基本的なことですが、どんなパターンが実装可能かとか忘れちゃいがちだったりするので、バリエーション作ってまとめときます。

準備

まずはベースのclass。とにかく詰め込んでるのでかなり長いです。

MyDialogFragment.java

public class MyDialogFragment extends DialogFragment {
    private static final String TAG = MyDialogFragment.class.getSimpleName();

    private static Dialog dialog = null;

    private boolean isCancelable = true; // TODO: デフォルトを設定

    private String title = "";
    private String message = "";

    private String positiveLabel = "";
    private DialogInterface.OnClickListener onPositiveButtonClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.w(TAG, "click positive");
        }
    };

    private String negativeLabel = "";
    private DialogInterface.OnClickListener onNegativeButtonClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.v(TAG, "click negative");
        }
    };

    private String neutralLabel = "";
    private DialogInterface.OnClickListener onNeutralButtonClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.v(TAG, "click neutral");
        }
    };

    private CharSequence[] items = null;

    private int checkedSingleChoiceItemPosition = -1;
    private CharSequence[] singleChoiceItems = null;

    // items or singleChoiceItemsの選択
    private DialogInterface.OnClickListener onItemClickListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Log.v(TAG, "click item which: " + which);
            checkedSingleChoiceItemPosition = which;
        }
    };

    private boolean[] checkedMultiChoiceItemsPosition = null;
    private CharSequence[] multiChoiceItems = null;
    private DialogInterface.OnMultiChoiceClickListener onMultiChoiceItemClickListener = new DialogInterface.OnMultiChoiceClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which, boolean isChecked) {
            Log.v(TAG, "click item which: " + which + ", isChecked: " + isChecked);
            checkedMultiChoiceItemsPosition[which] = isChecked;
        }
    };

    private View dialogView = null;
    private CheckBox checkBox = null;
    private EditText editText = null;

    public MyDialogFragment() {
    }

    // キャンセル有効
    public MyDialogFragment validCancel() {
        this.isCancelable = true;
        return this;
    }

    // キャンセル無効
    public MyDialogFragment invalidCancel() {
        this.isCancelable = false;
        return this;
    }

    // タイトルを設定
    public MyDialogFragment setTitle(String title) {
        this.title = title;
        return this;
    }

    // メッセージを設定
    public MyDialogFragment setMessage(String message) {
        this.message = message;
        return this;
    }


    // Positiveな選択肢の表示名(設定しなくてもデフォルト値で表示)
    public MyDialogFragment setPositiveLabel(String positiveLabel) {
        this.positiveLabel = positiveLabel;
        return this;
    }

    // Positive選択時の処理
    public MyDialogFragment setOnPositiveButtonClickListener(DialogInterface.OnClickListener listener) {
        this.onPositiveButtonClickListener = listener;
        return this;
    }


    // Negativeな選択肢の表示名
    public MyDialogFragment setNegativeLabel(String negativeLabel) {
        this.negativeLabel = negativeLabel;
        return this;
    }

    // Negative選択時の処理
    public MyDialogFragment setOnNegativeButtonClickListener(DialogInterface.OnClickListener listener) {
        this.onNegativeButtonClickListener = listener;
        return this;
    }


    // Neutralな選択肢の表示名
    public MyDialogFragment setNeutralLabel(String neutralLabel) {
        this.neutralLabel = neutralLabel;
        return this;
    }

    // Neutral選択時の処理
    public MyDialogFragment setOnNeutralButtonClickListener(DialogInterface.OnClickListener listener) {
        this.onNeutralButtonClickListener = listener;
        return this;
    }


    // リストを設置
    public MyDialogFragment setItems(CharSequence[] items) {
        this.items = items;
        return this;
    }

    // リストでアイテム選択時の処理
    public MyDialogFragment setOnItemClickListener(DialogInterface.OnClickListener listener) {
        this.onItemClickListener = listener;
        return this;
    }


    // SingleChoiceItemsを設置
    public MyDialogFragment setSingleChoiceItems(CharSequence[] singleChoiceItems, int checkedItemPosition) {
        this.singleChoiceItems = singleChoiceItems;

        // 初期選択アイテムの位置
        this.checkedSingleChoiceItemPosition = checkedItemPosition;
        return this;
    }

    // SingleChoiceItemsのアイテム選択時の処理(必須ではない)
    public MyDialogFragment setOnSingleChoiceItemListener(DialogInterface.OnClickListener listener) {
        this.onItemClickListener = listener;
        return this;
    }

    // setOnSingleChoiceItemListenerを自分で設定した場合に、保持するポジションも変更
    public MyDialogFragment setCheckedSingleChoiceItem(int position) {
        this.checkedSingleChoiceItemPosition = position;
        return this;
    }

    // SingleChoiceItemsで選択中のアイテムの位置
    public int getCheckedSingleChoiceItemPosition() {
        return this.checkedSingleChoiceItemPosition;
    }

    // SingleChoiceItemsで選択中のアイテム
    public String getCheckedSingleChoiceItem() {
        return this.singleChoiceItems[getCheckedSingleChoiceItemPosition()].toString();
    }


    // MultiChoiceItemsを設置
    public MyDialogFragment setMultiChoiceItems(CharSequence[] multiChoiceItems, boolean[] checkedMultiChoiceItems) {
        this.multiChoiceItems = multiChoiceItems;

        // 初期選択アイテムの位置
        this.checkedMultiChoiceItemsPosition = checkedMultiChoiceItems;
        return this;
    }

    // MultiChoiceItemsのアイテム選択時の処理(必須ではない)
    public MyDialogFragment setOnMultiChoiceItemListener(DialogInterface.OnMultiChoiceClickListener onMultiChoiceItemClickListener) {
        this.onMultiChoiceItemClickListener = onMultiChoiceItemClickListener;
        return this;
    }

    // setOnMultiChoiceItemListenerを自分で設定した場合に、保持するポジションも変更
    public MyDialogFragment setCheckedMultiChoiceItem(int position, boolean isChecked) {
        this.checkedMultiChoiceItemsPosition[position] = isChecked;
        return this;
    }

    // MultiChoiceItemsで選択中のアイテムの位置
    public boolean[] getCheckedMultiChoiceItemsPosition() {
        return this.checkedMultiChoiceItemsPosition;
    }

    // MultiChoiceItemsで選択中のアイテム
    public List<String> getCheckedMultiChoiceItems() {
        List<String> items = new ArrayList<>();
        for (int i = 0; i < this.multiChoiceItems.length; i++) {
            if (this.checkedMultiChoiceItemsPosition[i]) {
                items.add(this.multiChoiceItems[i].toString());
            }
        }
        return items;
    }


    // 指定のレイアウトを設置
    public MyDialogFragment setView(Context c, int layoutId) {
        LayoutInflater adbInflater = LayoutInflater.from(c);
        this.dialogView = adbInflater.inflate(layoutId, null);
        return this;
    }

    // 画像を設置
    public MyDialogFragment setImageView(Context c, int drawableId) {
        LayoutInflater adbInflater = LayoutInflater.from(c);
        this.dialogView = adbInflater.inflate(R.layout.dialog_parts_iv, null);
        ImageView iv = (ImageView) dialogView.findViewById(R.id.iv);
        iv.setImageResource(drawableId);
        return this;
    }


    // EditTextの設置
    public MyDialogFragment setEditText(Context c, String initialValue) {
        LayoutInflater adbInflater = LayoutInflater.from(c);
        this.dialogView = adbInflater.inflate(R.layout.dialog_parts_et, null);
        this.editText = (EditText) dialogView.findViewById(R.id.et);
        this.editText.setText(initialValue);
        return this;
    }

    // EditTextの入力値
    public String getEditText() {
        if (this.editText == null) {
            return "";
        }
        return this.editText.getText().toString();
    }


    // 数値限定のEditTextを設置
    public MyDialogFragment setEditTextInputNumber(Context c, int initialValue) {
        setEditText(c, String.valueOf(initialValue));
        this.editText.setInputType(InputType.TYPE_CLASS_NUMBER);
        return this;
    }

    // 数値限定のEditTextの入力値
    public int getEditTextInt() {
        if (this.editText == null) {
            return 0;
        }
        return Integer.parseInt(this.editText.getText().toString());
    }


    // チェックボックスの設置
    public MyDialogFragment setCheckBox(Context c) {
        LayoutInflater adbInflater = LayoutInflater.from(c);
        this.dialogView = adbInflater.inflate(R.layout.dialog_parts_checkbox, null);
        checkBox = (CheckBox) dialogView.findViewById(R.id.checkbox);
        return this;
    }

    // チェックボックスのチェック
    public boolean getCheckBox() {
        if (checkBox == null) {
            return false;
        }
        return checkBox.isChecked();
    }


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        setCancelable(isCancelable); // Fragmentに対して設定

        AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(getActivity());
        if (!title.equals("")) {
            dlgBuilder.setTitle(title);
        }
        if (!message.equals("")) {
            dlgBuilder.setMessage(message);
        }
        if (items != null) {
            dlgBuilder.setItems(items, onItemClickListener);
        }
        if (singleChoiceItems != null) {
            dlgBuilder.setSingleChoiceItems(singleChoiceItems, checkedSingleChoiceItemPosition, onItemClickListener);
        }

        if (multiChoiceItems != null) {
            dlgBuilder.setMultiChoiceItems(multiChoiceItems, checkedMultiChoiceItemsPosition, onMultiChoiceItemClickListener);
        }
        if (dialogView != null) {
            dlgBuilder.setView(dialogView);
        }

        // positiveLabelは空でもデフォルト値を指定して表示
        if (positiveLabel.equals("")) {
            positiveLabel = getString(R.string.dialog_positive); // TODO: デフォルトを設定
        }
        if (items == null) { // setItemの場合にボタンが必要ならNegativeをセット
            dlgBuilder.setPositiveButton(positiveLabel, onPositiveButtonClickListener);
        }

        // Negativeは指定がなければ非表示
        if (!negativeLabel.equals("")) {
            dlgBuilder.setNegativeButton(negativeLabel, onNegativeButtonClickListener);
        }

        // Neutralは指定がなければ非表示
        if (!neutralLabel.equals("")) {
            dlgBuilder.setNeutralButton(neutralLabel, onNeutralButtonClickListener);
        }
        dlgBuilder.setCancelable(isCancelable); // 多分、意味ないけど一応

        dialog = dlgBuilder.create();
        dialog.setCanceledOnTouchOutside(isCancelable); // ダイアログ外タップもsetCancelableと共通で設定
        return dialog;
    }

    @Override
    public void onDestroyView() {
        if (getDialog() != null && getRetainInstance()) {
            getDialog().setDismissMessage(null);
        }
        super.onDestroyView();
    }

    // ダイアログを非表示にする
    public static void dissmissDialog() {
        if (dialog != null && dialog.isShowing()) {
            dialog.dismiss();
        }
    }
}

このclassで使っているlayout.xmlをいくつか

res/layout/dialog_parts_iv.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:paddingLeft="@dimen/mg_l"
    android:paddingRight="@dimen/mg_l">

    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/empty"/>

</LinearLayout>

res/layout/dialog_parts_et.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="@dimen/mg_l"
    android:paddingRight="@dimen/mg_l">

    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"/>

</LinearLayout>

res/layout/dialog_parts_checkbox.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="@dimen/mg_m"
    android:paddingRight="@dimen/mg_m">

    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/dialog_checkbox"/>

</LinearLayout>

実装

アラートダイアログ

とりあえず最小限の構成で

new MyDialogFragment()
        .setMessage("メッセージ")
        .show(getFragmentManager(), "myDialogTag");

setMessage()を、setTitle()にしてもらってもいいかも
Activity以外ではgetFragmentManager()前にActivityをセットする(以降は省略)

new MyDialogFragment()
        .setMessage("メッセージ")
        .show(((Activity) context).getFragmentManager(), "myDialogTag");

確認ダイアログ

適宜、Positive、Negative、Neutralのラベルやクリックリスナーを設定
内容によってはキャンセルできなくするとか

new MyDialogFragment()
        .invalidCancel()
        .setTitle("タイトル")
        .setMessage("よいでしょうか?")
        .setPositiveLabel("Yes")
        .setOnPositiveButtonClickListener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getApplicationContext(), "Yes", Toast.LENGTH_SHORT).show();
            }
        })
        .setNegativeLabel("No")
        .setOnNegativeButtonClickListener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getApplicationContext(), "No", Toast.LENGTH_SHORT).show();
            }
        })
        .show(getFragmentManager(), "myDialogTag");

リストから選択

選択肢を用意してユーザーが選ぶ

final String[] items = {"選択肢1", "選択肢2", "選択肢3", "選択肢4"};

new MyDialogFragment()
        .setTitle("setItems")
        .setItems(items)
        .setOnItemClickListener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getApplicationContext(), items[which], Toast.LENGTH_SHORT).show();
            }
        })
        .show(getFragmentManager(), "myDialogTag");

screenshot_20161103-015426

ラジオボタン付きリスト

性能的には前のリストと近いけど、前回の設定とかを見せるのにはこっちの方が便利

final String[] items = {"選択肢1", "選択肢2", "選択肢3", "選択肢4"};
final int checkedItem = 0; // 未選択は-1

final MyDialogFragment mDialog = new MyDialogFragment();
mDialog
        .setTitle("setSingleChoiceItems")
        .setSingleChoiceItems(items, checkedItem)
        .setOnSingleChoiceItemListener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                mDialog.setCheckedSingleChoiceItem(which);
                Toast.makeText(getApplicationContext(), mDialog.getCheckedSingleChoiceItem() + "を選択", Toast.LENGTH_SHORT).show();
            }
        })
        .setOnPositiveButtonClickListener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getApplicationContext(), mDialog.getCheckedSingleChoiceItem(), Toast.LENGTH_SHORT).show();
            }
        })
        .show(getFragmentManager(), "myDialogTag");

screenshot_20161103-015434

チェックボックス付きリスト

複数、選べる

final String[] items = {"選択肢1", "選択肢2", "選択肢3", "選択肢4"};
final boolean[] checked = {true, false, true, false}; // 初期値が未選択ならnullでOK

final MyDialogFragment mDialog = new MyDialogFragment();
mDialog
        .setTitle("setMultiChoiceItems")
        .setMultiChoiceItems(items, checked)
        .setOnMultiChoiceItemListener(new DialogInterface.OnMultiChoiceClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                Log.v(TAG, "click item which: " + which + ", isChecked: " + isChecked);
                mDialog.setCheckedMultiChoiceItem(which, isChecked);
            }
        })
        .setOnPositiveButtonClickListener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String checkedItems = "";
                for (int i = 0; i < mDialog.getCheckedMultiChoiceItems().size(); i++) {
                    checkedItems += mDialog.getCheckedMultiChoiceItems().get(i) + " ";
                }
                if (checkedItems.equals("")) {
                    checkedItems = "未選択";
                }
                Toast.makeText(getApplicationContext(), checkedItems, Toast.LENGTH_SHORT).show();
            }
        })
        .show(getFragmentManager(), "myDialogTag");

screenshot_20161103-015439

ImageViewを設置

ダイアログに作成したレイアウトを設置
今回はImageViewを置いて画像を表示

new MyDialogFragment()
        .setTitle("ImageViewを設置")
        .setImageView(this, R.mipmap.ic_launcher)
        .show(getFragmentManager(), "myDialogTag");

screenshot_20161103-015444

EditTextを設置

今度はEditTextを配置

final MyDialogFragment mDialog = new MyDialogFragment();
mDialog
        .setTitle("EditTextを設置")
        .setEditText(this, "初期値")
        .setOnPositiveButtonClickListener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast.makeText(getApplicationContext(), "input: " + mDialog.getEditText(), Toast.LENGTH_SHORT).show();
            }
        })
        .show(getFragmentManager(), "myDialogTag");

screenshot_20161103-015448

CheckBoxを設置

更にチェックボックスを配置

final MyDialogFragment mDialog = new MyDialogFragment();
mDialog
        .setTitle("CheckBoxを設置")
        .setCheckBox(this)
        .setOnPositiveButtonClickListener(new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                boolean check = mDialog.getCheckBox();
                Toast.makeText(getApplication(), "check: " + check, Toast.LENGTH_SHORT).show();
            }
        })
        .show(getFragmentManager(), "myDialogTag");

screenshot_20161103-015454

 

とりあえずこんな感じで