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