ページ

2013/05/02

[Android]DialogFragmentの汎用性を高めたAlertDialogFragment

[Android]DialogFragmentの罠を回避するAlertDialogFragment | DevAchieve
なかなか良いと思ったがそれぞれのダイアログで微妙に表示したい形式が違うと
それぞれクラスを用意しなければならず面倒だったので頑張って汎用的にしてみた。

AlertDailog.Builder でできることの一部を実装済みだけど
設定できるのは他にも色々あって使わないのはよくわからないので
Pull request してくれたら嬉しいです。

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;

public class AlertDialogFragment extends DialogFragment implements OnClickListener {

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // getter/setter用キーの定義
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    private static final String KEY_DIALOG_ICON                 = "dialogIcon";
    private static final String KEY_DIALOG_TITLE                = "dialogTitle";
    private static final String KEY_DIALOG_MESSAGE              = "dialogText";
    private static final String KEY_DIALOG_TITLE_VIEW           = "dialogTitleView";
    private static final String KEY_DIALOG_CONTENT_VIEW         = "dialogContentView";
    private static final String KEY_DIALOG_POSITIVE_BUTTON_TEXT = "dialogPositiveButtonText";
    private static final String KEY_DIALOG_NEUTRAL_BUTTON_TEXT  = "dialogNeutralButtonText";
    private static final String KEY_DIALOG_NEGATIVE_BUTTON_TEXT = "dialogNegativeButtonText";

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // getString系のonAttachまでの一時保持用変数
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    private static final int    DEFAULT_INT_VALUE               = 0;
    private int                 mTitleId                        = DEFAULT_INT_VALUE;
    private int                 mMessageId                      = DEFAULT_INT_VALUE;
    private int                 mPositiveButtonTextId           = DEFAULT_INT_VALUE;
    private int                 mNeutralButtonTextId            = DEFAULT_INT_VALUE;
    private int                 mNegativeButtonTextId           = DEFAULT_INT_VALUE;

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // インスタンス生成とsetArguments(※setArgumentsしないとgetArgumentsでNPE)
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    public static AlertDialogFragment newInstance(){
        AlertDialogFragment alertDialogFragment = new AlertDialogFragment();
        Bundle args = new Bundle();
        alertDialogFragment.setArguments(args);
        return alertDialogFragment;
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // activityにアタッチされていなければ使えないgetString系とリスナーのセット
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    @Override
    public void onAttach(Activity activity){
        super.onAttach(activity);
        if(mTitleId != DEFAULT_INT_VALUE){
            setTitle(getString(mTitleId));
        }
        if(mMessageId != DEFAULT_INT_VALUE){
            setMessage(getString(mMessageId));
        }
        if(mPositiveButtonTextId != DEFAULT_INT_VALUE){
            setPositiveButtonText(getString(mPositiveButtonTextId));
        }
        if(mNeutralButtonTextId != DEFAULT_INT_VALUE){
            setNeutralButtonText(getString(mNeutralButtonTextId));
        }
        if(mNegativeButtonTextId != DEFAULT_INT_VALUE){
            setNegativeButtonText(getString(mNegativeButtonTextId));
        }
        if(activity instanceof DialogListener == false){
            throw new ClassCastException("Activity not implements DialogListener.");
        }
        mListener = (DialogListener)activity;
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // AlertDialog.Builder
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState){
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setIcon(getIcon());
        builder.setTitle(getTitle());
        builder.setMessage(getMessage());
        int titleViewId = getTitleViewId();
        if(titleViewId != DEFAULT_INT_VALUE){
            LayoutInflater inflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            builder.setCustomTitle(inflater.inflate(titleViewId, null));
        }
        int contentViewId = getContentViewId();
        if(contentViewId != DEFAULT_INT_VALUE){
            LayoutInflater inflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            builder.setView(inflater.inflate(contentViewId, null));
        }
        String positiveButtonText = getPositiveButtonText();
        if(positiveButtonText != null){
            builder.setPositiveButton(positiveButtonText, this);
        }
        String neutralButtonText = getNeutralButtonText();
        if(neutralButtonText != null){
            builder.setNeutralButton(neutralButtonText, this);
        }
        String negativeButtonText = getNegativeButtonText();
        if(negativeButtonText != null){
            builder.setNegativeButton(negativeButtonText, this);
        }
        return builder.create();
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // DialogListener
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    private DialogListener mListener = null;

    public interface DialogListener {

        public void onPositiveButtonClick(String tag);

        public void onNeutralButtonClick(String tag);

        public void onNegativeButtonClick(String tag);

        public void onDialogCancel(String tag);

        public void onDialogDismiss(String tag);

    }

    @Override
    public void onClick(DialogInterface dialog, int which){
        switch(which){
            case DialogInterface.BUTTON_POSITIVE:
                if(mListener != null){
                    mListener.onPositiveButtonClick(getTag());
                }
                break;
            case DialogInterface.BUTTON_NEUTRAL:
                if(mListener != null){
                    mListener.onNeutralButtonClick(getTag());
                }
                break;
            case DialogInterface.BUTTON_NEGATIVE:
                if(mListener != null){
                    mListener.onNegativeButtonClick(getTag());
                }
                break;
            default:
                break;
        }
    }

    @Override
    public void onCancel(DialogInterface dialog){
        super.onCancel(dialog);
        if(mListener != null){
            mListener.onDialogCancel(getTag());
        }
    }

    @Override
    public void onDismiss(DialogInterface dialog){
        super.onDismiss(dialog);
        if(mListener != null){
            mListener.onDialogDismiss(getTag());
        }
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // getter/setter
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    private int getIcon(){
        return getArguments().getInt(KEY_DIALOG_ICON, DEFAULT_INT_VALUE);
    }

    public void setIcon(int resId){
        getArguments().putInt(KEY_DIALOG_ICON, resId);
    }

    private String getTitle(){
        return getArguments().getString(KEY_DIALOG_TITLE);
    }

    public void setTitle(int resId){
        mTitleId = resId;
    }

    public void setTitle(String title){
        getArguments().putString(KEY_DIALOG_TITLE, title);
    }

    private String getMessage(){
        return getArguments().getString(KEY_DIALOG_MESSAGE);
    }

    public void setMessage(int resId){
        mMessageId = resId;
    }

    public void setMessage(String text){
        getArguments().putString(KEY_DIALOG_MESSAGE, text);
    }

    private int getTitleViewId(){
        return getArguments().getInt(KEY_DIALOG_TITLE_VIEW, DEFAULT_INT_VALUE);
    }

    public void setTitleViewId(int resId){
        getArguments().putInt(KEY_DIALOG_TITLE_VIEW, resId);
    }

    private int getContentViewId(){
        return getArguments().getInt(KEY_DIALOG_CONTENT_VIEW, DEFAULT_INT_VALUE);
    }

    public void setContentViewId(int resId){
        getArguments().putInt(KEY_DIALOG_CONTENT_VIEW, resId);
    }

    private String getPositiveButtonText(){
        return getArguments().getString(KEY_DIALOG_POSITIVE_BUTTON_TEXT);
    }

    public void setPositiveButtonText(int resId){
        mPositiveButtonTextId = resId;
    }

    public void setPositiveButtonText(String text){
        getArguments().putString(KEY_DIALOG_POSITIVE_BUTTON_TEXT, text);
    }

    private String getNeutralButtonText(){
        return getArguments().getString(KEY_DIALOG_NEUTRAL_BUTTON_TEXT);
    }

    public void setNeutralButtonText(int resId){
        mNeutralButtonTextId = resId;
    }

    public void setNeutralButtonText(String text){
        getArguments().putString(KEY_DIALOG_NEUTRAL_BUTTON_TEXT, text);
    }

    private String getNegativeButtonText(){
        return getArguments().getString(KEY_DIALOG_NEGATIVE_BUTTON_TEXT);
    }

    public void setNegativeButtonText(int resId){
        mNegativeButtonTextId = resId;
    }

    public void setNegativeButtonText(String text){
        getArguments().putString(KEY_DIALOG_NEGATIVE_BUTTON_TEXT, text);
    }
}
getter/setter で Arguments を参照/変更すれば画面回転で破棄されて再生成しても問題ない。
呼び出しは以下のようにする。
Fragment prevDialogFragment = getSupportFragmentManager().findFragmentByTag(TAG_DIALOG_INFO);
if(prevDialogFragment == null){
    AlertDialogFragment dialogFragment = AlertDialogFragment.newInstance();
    dialogFragment.setTitleViewId(R.layout.dialog_info_title);
    dialogFragment.setContentViewId(R.layout.dialog_info_view);
    dialogFragment.setPositiveButtonText(R.string.dialogClose);
    dialogFragment.show(getSupportFragmentManager(), TAG_DIALOG_INFO);
}
タグでリスナーのコールバックでどのダイアログに対するイベントなのか判別できる。
前回と呼び出しが違うのは DialogFragment が dismiss した際に内部で BackStack を pop しているので
いちいち remove する必要がないということがわかったため。
前回のコードはすでにあったら破棄して再表示だったので
重いダイアログは2回表示されるのが見えていたけど、
今回は prevDialogFragment があったら(ダイアログ表示ボタン連打などで消す前に表示していたら)
何もしないで現在表示中のものを表示しておくという処理になっている。