概要
AndroidでDialogを実装するとき、Activityにinner classとしてDialogFragmentを実装する方法をよく使っていましたが、Activityのステップ数が多くなってしまうのと、Dialogがどこにあるのかわかりづらいと感じていたので、Dialogの実装を統一化するプラクティスを考えました。
詳細
Activity内にDialogを実装するのではなくDialog表示のためのFactoryを使う。
DialogのshowもActivityではなく共通クラスに任せる。
ソースコードはGitHubより取得お願いします。
https://github.com/ken-maki/k_commons.dialog
構成は以下。
com.android.k.commons.dialog
┣lib
┃┣BaseDialogFactory
┃┃ DialogのBaseFactoryクラス、Dialog生成に関する共通的な実装はここに書く。
┃┣AlertDialogFactory
┃┃ AlertDialog実装用の親Factoryクラス、AlertDialogを新規作成するときはこのクラスを継承する。
┃┣DialogGenerator
┃┃ Dialog表示を担うクラス、Factoryを渡してあげてDialogさせる。
┃┃ Dialog生成時の制御(例えば、重複表示を抑止)の実装はここに書く。
┃┗CommonAlertDialog
┃ AlertDialog本体。引数に取ったFactoryからBuilderを取得しonCreateDialogのBuilderとして使う。
┣mydialog
┃┣MySampleFooDialogFactory
┃┃ サンプル用DialogFactory、ボタンのクリックイベントをFactory内で実装するパターン。
┃┗MySampleBarDialogFactory
┃ サンプル用DialogFactory、ボタンのクリックイベントをFactoryに設定されたリスナで実装するパターン。
┗MainActivity
ダイアログ表示のサンプルActivity。
MySampleBarDialogFactory表示のときに自前でOnClickListenerを実装する。
ソースの解説
ライブラリ部分
abstract class BaseDialogFactory implements Serializable {
DialogInterface.OnCancelListener mOnCancelListener;
DialogInterface.OnDismissListener mOnDismissListener;
/**
* Activity to display Dialog.
*/
private Activity mActivity;
/**
* Constructor.
*
* @param activity create dialog for activity.
*/
BaseDialogFactory(Activity activity) {
if (activity == null) throw new IllegalArgumentException("activity is null.");
mActivity = activity;
}
/**
* get Activity.
*
* @return Activity
*/
protected Activity getActivity() {
return mActivity;
}
/**
* get Dialog Tag.
*
* @return Dialog Tag
*/
protected String getTag() {
return this.getClass().getSimpleName();
}
/**
* set OnCancelListener.
* @param listener action listener
*/
public void setOnCancelListener(DialogInterface.OnCancelListener listener) {
mOnCancelListener = listener;
}
/**
* set OnDismissListener.
* @param listener action listener
*/
public void setOnDismissListener(DialogInterface.OnDismissListener listener) {
mOnDismissListener = listener;
}
}
DialogFactoryの親クラス、共通的な処理を実装しています。
public abstract class AlertDialogFactory extends BaseDialogFactory {
protected DialogInterface.OnClickListener mPositiveClickListener;
protected DialogInterface.OnClickListener mNegativeClickListener;
/**
* Constructor.
*
* @param activity create dialog for activity.
*/
public AlertDialogFactory(Activity activity) {
super(activity);
}
/**
* create AlertDialog.Builder.
*
* @return can be Show AlertDialog.Builder
*/
public abstract AlertDialog.Builder build();
/**
* set OnClickListener for PositiveButton click.
* @param listener action listener
*/
public void setPositiveOnClickListener(DialogInterface.OnClickListener listener) {
mPositiveClickListener = listener;
}
/**
* set OnClickListener for NegativeButton click.
* @param listener action listener
*/
public void setNegativeClickListener(DialogInterface.OnClickListener listener) {
mNegativeClickListener = listener;
}
}
AlertDialogのFactoryクラス。親build()メソッドがこのクラスの主役です。
AlertDialogFactoryを継承したクラスは、build()メソッド内に表示したい情報の
AlertDialog.Builderを生成、返却する実装を作成するだけでOK。
もしAlertDialog以外のDialogを使いたいときはAlertDialogFactoryのような感じで
親Factoryを作成する。(SingleChoiceDialogFactoryとかDatePickerDialogFactoryとか)
public class CommonAlertDialog extends DialogFragment {
private static final String ARG_KEY_FACTORY = "f";
AlertDialogFactory mFactory;
public static CommonAlertDialog newInstance(AlertDialogFactory factory) {
Bundle bundle = new Bundle();
bundle.putSerializable(ARG_KEY_FACTORY, factory);
CommonAlertDialog dialog = new CommonAlertDialog();
dialog.setArguments(bundle);
return dialog;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mFactory = (AlertDialogFactory)
getArguments().getSerializable(ARG_KEY_FACTORY);
if (mFactory == null) throw new IllegalArgumentException("factory is null.");
AlertDialog.Builder builder = mFactory.build();
if (builder == null) throw new IllegalStateException("AlertDialog.Builder is null.");
return builder.create();
}
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
if (mFactory.mOnCancelListener != null) {
mFactory.mOnCancelListener.onCancel(dialog);
}
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (mFactory.mOnDismissListener != null) {
mFactory.mOnDismissListener.onDismiss(dialog);
}
}
/**
* return AlertDialogFactory.
* @return AlertDialogFactory
*/
public AlertDialogFactory getFactory() {
return mFactory;
}
}
Dialog本体。
やってることは大まかに以下の2つ。
・引数からFactoryをとってきて、createする。
・DialogInterfaceのイベントが設定されてたら実行する。
個々の実装部分
public class MySampleFooDialogFactory extends AlertDialogFactory {
public MySampleFooDialogFactory(Activity activity) {
super(activity);
}
@Override
public AlertDialog.Builder build() {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Foo Dialog");
builder.setMessage("This Foo Dialog. please Button Click.");
builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getActivity(), "Ok clicked.(listener setting from factory)", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getActivity(), "Cancel clicked.(listener setting from factory)", Toast.LENGTH_SHORT).show();
dialog.dismiss();
}
});
builder.setCancelable(false);
return builder;
}
}
AlertDialogFactoryを継承してbuild()を実装するだけ。
サンプルは簡素な実装ですが、カスタムレイアウトなどを使う複雑なDialogのときも
処理はbuild()に実装。
こうすることでDialog作成時はDialogの中身を作ることだけに集中できる。
メリット
-Dialog実装する人はAlertDialog.Builderを生成するコードの実装だけに集中できる。
共通処理はDialogGeneratorなりAlertDialogFactoryなりCommonAlertDialogなりに
実装することで、AlertDialog全体が統一された動作になる。
-Activity内に直接Dialogの実装とDialogをshowする実装が無くなる
Activityと疎結合になるのでinner classでの実装に比べDialog側の変更がしやすくなるはず。
終わりに
Qiita初投稿でしたが、こんな感じの記事でいいのかなぁ。
試行錯誤で今後も記事投稿していきますので、生暖かい目で見守ってくださると幸いです。