DialogFragmentの罠
setArgumentsしないと画面回転で値が保持されない
public static AlertDialogFragment newInstance(int iconId, int titleId, int textId){ AlertDialogFragment alertDialogFragment = new AlertDialogFragment(); Bundle args = new Bundle(); args.putInt(KEY_DIALOG_ICON, iconId); args.putInt(KEY_DIALOG_TITLE, titleId); args.putInt(KEY_DIALOG_TEXT, textId); args.putInt(KEY_DIALOG_POSITIVE_BUTTON_TEXT, mPositiveButtonTextId); alertDialogFragment.setArguments(args); return alertDialogFragment; }Fragment 関係で newInstance メソッドをよく見る理由は
Fragment が再生成される際にデフォルトコンストラクタが呼び出されるので
newInstance メソッドに引数渡してその中で setArguments の使用を強制させるためらしい。
ということで上記のように記述すれば問題ない。
setterなどで追加設定しても画面回転で値が保持されない
public void setPositiveButtonText(int positiveButtonTextId){ mPositiveButtonTextId = positiveButtonTextId; getArguments().putInt(KEY_DIALOG_POSITIVE_BUTTON_TEXT, mPositiveButtonTextId); }通常の setter で追加設定しても画面回転などで DialogFragment が再生成されると
新しいインスタンスを生成するっぽいのでメンバ変数に保持しても意味が無い。
arguments から復元しているっぽいので後から追加しておけば良いんじゃねーのとやってみたら保持された。
ボタンクリックのコールバックリスナーが画面回転で保持されない
public static interface DialogListener { /** * Positive ボタンが押されたイベントを通知する */ public void onPositiveButtonClick(); } @Override public void onAttach(Activity activity){ super.onAttach(activity); if(activity instanceof DialogListener == false){ throw new ClassCastException("Activity not implements DialogListener."); } mListener = (DialogListener)activity; } @Override public void onClick(DialogInterface dialog, int which){ if(mListener != null){ mListener.onPositiveButtonClick(); } }onAttach でメンバ変数にセットしなおしてやれば問題ない。
呼び出し側の Fragment で DialogListener を implements してやれば良い。
参考
Y.A.M の 雑記帳: Android Fragment で setArguments() してるサンプルが多いのはなぜ?ネタ帳 A.B.C: DialogFragmentメモ(2)
夜でもアッサム: DialogFragmentの落とし穴にはまらないための方法
コジオニルク - Android - DialogFragment
ソースコード
import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.os.Bundle; import android.support.v4.app.DialogFragment; public class AlertDialogFragment extends DialogFragment { public static interface DialogListener { /** * Positive ボタンが押されたイベントを通知する */ public void onPositiveButtonClick(); } /** 引数受け渡し用のキー */ private static final String KEY_DIALOG_ICON = "dialogIcon"; private static final String KEY_DIALOG_TITLE = "dialogTitle"; private static final String KEY_DIALOG_TEXT = "dialogText"; private static final String KEY_DIALOG_POSITIVE_BUTTON_TEXT = "dialogPositiveButtonText"; /** Positive ボタンのボタンテキストのリソースID */ private static int mPositiveButtonTextId = android.R.string.ok; /** ボタンのクリックを通知するリスナー */ private DialogListener mListener = null; public static AlertDialogFragment newInstance(int iconId, int titleId, int textId){ AlertDialogFragment alertDialogFragment = new AlertDialogFragment(); Bundle args = new Bundle(); args.putInt(KEY_DIALOG_ICON, iconId); args.putInt(KEY_DIALOG_TITLE, titleId); args.putInt(KEY_DIALOG_TEXT, textId); args.putInt(KEY_DIALOG_POSITIVE_BUTTON_TEXT, mPositiveButtonTextId); alertDialogFragment.setArguments(args); return alertDialogFragment; } public void setPositiveButtonText(int positiveButtonTextId){ mPositiveButtonTextId = positiveButtonTextId; getArguments().putInt(KEY_DIALOG_POSITIVE_BUTTON_TEXT, mPositiveButtonTextId); } @Override public void onAttach(Activity activity){ super.onAttach(activity); if(activity instanceof DialogListener == false){ throw new ClassCastException("Activity not implements DialogListener."); } mListener = (DialogListener)activity; } @Override public Dialog onCreateDialog(Bundle savedInstanceState){ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setIcon(getArguments().getInt(KEY_DIALOG_ICON)); builder.setTitle(getArguments().getInt(KEY_DIALOG_TITLE)); builder.setMessage(getArguments().getInt(KEY_DIALOG_TEXT)); builder.setPositiveButton(mPositiveButtonTextId, new OnClickListener(){ @Override public void onClick(DialogInterface dialog, int which){ if(mListener != null){ mListener.onPositiveButtonClick(); } } }); return builder.create(); } }
おまけ: 公式の罠?
DialogFragment | Android Developers で紹介されているサンプルでprev を remove する FragmentTransaction が commit されていないので 何も起こらない?
連打すると普通に2つくらいダイアログが表示されるが、commit すると1つしか表示されないようになる。
private void showDailog(){ FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); Fragment prevDialogFragment = getSupportFragmentManager().findFragmentByTag(TAG_DIALOG_INFO); if(prevDialogFragment != null){ fragmentTransaction.remove(prevDialogFragment); } fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); AlertDialogFragment dialogFragment = AlertDialogFragment.newInstance(android.R.drawable.ic_dialog_info, R.string.dialogTitleInfo, R.string.dialogTextInfo); dialogFragment.setPositiveButtonText(R.string.dialogClose); dialogFragment.show(getSupportFragmentManager(), TAG_DIALOG_INFO); }
※追記(2013/05/02)
AlertDialogFragment をもっと汎用的にしました。[Android]DialogFragmentの汎用性を高めたAlertDialogFragment | DevAchieve