DialogFragment的简单分析和封装使用

初识DialogFragment

DialogFragment是Android 3.0引入,Google官方更加推荐使用DialogFragment来代替Dialog使用。原因与推荐Fragment类似,使用DialogFragment归根结底也是使用Fragment,它具有相对完整的生命周期并且由FragmentManager控制,当遇到特殊情况,例如转屏后,传统Dialog将会消失,而使用DialogFragment,onCreate方法重新调用,并获取arguments。这种方式创建的对话框会再次生成。

DialogFragment的生命周期

首先,Fragment生命周期参考此图,不再赘述。
Fragment生命周期

其实DialogFragment作为其子类,生命周期类似,重点是多了一个onCreateDialog方法,经过Log输出,确定创建时调用顺序为:

onCreate –> onCreateDialog –> onCreateView

接下来细化各生命周期调用过程:

  • onCreate方法中主要是完成Fragmen重创建过程,如果savedInstanceState不为空则从savedInstanceState恢复由于转屏等引起的已销毁的Fragment。
  • onCreateDialog方法在getLayoutInflater中调用,向上追溯是FragmentManager在performCreateView时调用。如果onCreateDialog没有被子类override则直接new出一个默认的Dialog,因此在onCreateDialog方法中应该由我们自定义如何创建Dialog,比如利用AlertDialog.Builder。
  • onCreateView。该方法是继承Fragment必复写的,可是在DialogFragment中则有所不同,如果你同时复写onCreateView和onCreateDialog则有可能会发生一些奇怪的Exception,参见http://stackoverflow.com/questions/13257038/custom-layout-for-dialogfragment-oncreateview-vs-oncreatedialog。建议使用DialogFragment时只选择使用onCreateDialog,将inflate和findview的过程也全部放在onCreateDialog中即可。

除此外,DialogFragment有别于普通Fragment的地方在于多了show、dismiss等方法,查看源码发现其实是多了一层转调,像show(FragmentManager manager, String tag)是传入FragmentManager后,add自身这个Fragment并commit。remove方法类似,转调dismissInternal,如果add了backStack则pop出栈,否则remove该Fragment。

自定义DialogFragment实例

在这里写了一个基类BaseDialogFragment,作为各种提示型Dialog的父类。它继承自DialogFragment,本身主要是根据子类传递的Layout建立Dialog并加以点击事件。点击响应和Button的自定义则通过接口和抽象方法,由子类去实现。

基类代码如下:

public abstract class BaseDialogFragment extends DialogFragment {
    protected int mLayout;
    protected static final String TAG_ARG = "layout";
    protected static final int DEFAULT_COLOR = -1; //default

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        //Step1 build Dialog
        if (mLayout == 0) {
            throw new RuntimeException("no correct layout found.");
        }
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        View view = LayoutInflater.from(getActivity()).inflate(mLayout, null);

        builder.setView(view).setPositiveButton(getOkText(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (mOnBtnClickListener != null)
                    mOnBtnClickListener.onOkClick();
            }
        }).setNegativeButton(getCancelText(), new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (mOnBtnClickListener != null)
                    mOnBtnClickListener.onCancelClick();
            }
        }).setCancelable(false);
        AlertDialog alertDialog = builder.create();

        //Step2 custom Button
        alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                //只有在Show之后才能getButton!
                Button okButton = ((AlertDialog) getDialog()).getButton(DialogInterface.BUTTON_POSITIVE);
                Button cancelButton = ((AlertDialog) getDialog()).getButton(DialogInterface.BUTTON_NEGATIVE);
                if (getOkTextColorRes() != DEFAULT_COLOR) {
                    okButton.setTextColor(getResources().getColor(getOkTextColorRes()));
                }
                if (getCancelTextColorRes() != DEFAULT_COLOR) {
                    cancelButton.setTextColor(getResources().getColor(getCancelTextColorRes()));
                }
                if (getOkBgColorRes() != DEFAULT_COLOR) {
                    okButton.setBackgroundResource(getOkBgColorRes());
                }
                if (getCancelBgColorRes() != DEFAULT_COLOR) {
                    cancelButton.setBackgroundResource(getCancelBgColorRes());
                }
            }
        });

        //Step3 request feature
        alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        return alertDialog;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }

    public interface OnBtnClickListener {
        public void onOkClick();

        public void onCancelClick();
    }

    private OnBtnClickListener mOnBtnClickListener;

    /**
     * 添加Button的Click监听
     *
     * @param onBtnClickListener
     */
    public void setOnBtnClickListener(OnBtnClickListener onBtnClickListener) {
        mOnBtnClickListener = onBtnClickListener;
    }

    /**
     * 用于设定Positive Button的Text颜色
     * @return Color的Resource Id
     */
    protected abstract int getOkTextColorRes();// text 只能是color

    /**
     * 用于设定Positive Button的背景颜色
     * @return Color或者Drawable的Resource Id
     */
    protected abstract int getOkBgColorRes();

    /**
     * 用于设定Negative Button的Text颜色
     * @return Color的Resource Id
     */
    protected abstract int getCancelTextColorRes();

    /**
     * 用于设定Negative Button的背景颜色
     * @return Color或者Drawable的Resource Id
     */
    protected abstract int getCancelBgColorRes();

    protected abstract String getOkText();

    protected abstract String getCancelText();

}

子类依据官方推荐的newInstance方式,传递参数并赋值给mLayout,这里的layout是必须要有的。剩下的就是自定义button。
注意Button的背景和字体颜色的自定义,本来想通过((AlertDialog) getDialog()).getButton(DialogInterface.BUTTON_POSITIVE)的方式拿到Button对象,但getDialog只有在这个Dialog显示出来后获取才不为null,所以这里选择在onShow方法的回调中处理Button的自定义。参考自http://stackoverflow.com/questions/17626228/alertdialog-getbutton-method-returns-null

子类代码:

public class UniDialogFragment extends BaseDialogFragment {

    /**
     * 根据布局文件,新建Fragment,传递argument
     *
     * @param layout
     * @return
     */
    public static BaseDialogFragment newInstance(int layout) {
        UniDialogFragment dialogFragment = new UniDialogFragment();
        Bundle args = new Bundle();
        args.putInt(TAG_ARG, layout);
        dialogFragment.setArguments(args);
        return dialogFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle arguments = getArguments();
        if (arguments != null) {
            mLayout = arguments.getInt(TAG_ARG, 0);
        }
    }

    @Override
    protected int getOkTextColorRes() {
        return R.color.common_blue_0B86E5;
    }

    @Override
    protected int getOkBgColorRes() {
        return R.drawable.selector_item_bg;
    }

    @Override
    protected int getCancelTextColorRes() {
        return BaseDialogFragment.DEFAULT_COLOR;
    }

    @Override
    protected int getCancelBgColorRes() {
        return R.drawable.selector_item_bg;
    }

    @Override
    protected String getOkText() {
        return "确定";
    }

    @Override
    protected String getCancelText() {
        return "取消";
    }
}

调用,注意如果创建过一次只需要show即可:

@Override
public void onClick(View v) {
    if (mDialogFragment == null) {
        mDialogFragment = UniDialogFragment.newInstance(R.layout.frg_custom_dialog);
        mDialogFragment.setOnBtnClickListener(new BaseDialogFragment.OnBtnClickListener() {
            @Override
            public void onOkClick() {
                ToastUtils.showShort(getApplicationContext(), "ok");
            }

            @Override
            public void onCancelClick() {
                ToastUtils.showShort(getApplicationContext(), "cancel");
            }
        });
    }
    mDialogFragment.show(getSupportFragmentManager(), "dialog");

}

效果如图:
UniDialogFragment show