注册

使用DialogFragment代替Dialog

使用DialogFragment代替Dialog

是这样,用了很久的一个Dialog工具类,结果今天发现了一个bug,尝试着搜索发现大家都已经用DialogFragment了,官方也推荐这么做,猛然醒悟原来自己已经过时这么久了。现在就来试试吧。

DialogFragment是什么

DialogFragment从它的源码得知,它继承了Fragment,其实是一个比较特殊的Fragment。那么它相对于普通的Dialog有什么不同,谷歌又为什么推荐我们使用它呢,它相对于普通的Dialog有什么优点呢。

使用过它之后用自己的感受描述:

  • 它的生命周期很清晰,方便写复杂的逻辑
  • 它于Activity的生命周期是绑定的,Activity消失,DialogFragment也会消失。
  • 它可以很简单的控制弹窗的布局。

总结就是dialogfragment能更好的管理dialog的展示与消失,以及在屏幕旋转时的一些状态保存问题。

DialogFragment的踩坑

即使它有很多的优点,但使用不当时,仍然会有很多坑。
我遇到了很多奇奇怪怪的问题。 比如

  • Fragment already added 异常
  • 快速的显示消失,无法消失的异常

当然除此之外我们还可能有以下需求:

  • 设置对话框的大小
  • 设置弹出对话框时背景灰色或者透明

下面我们就来一一实现。

如何实现DialogFragment

重点来关注两个方法。

  • onCreateDialog 新建一个Dialog即可使用
  • onCreateView 自定义一个Dialog界面

onCreateDialog

Screenshot_2021-11-04-22-04-55-27_2a27335eaa331505125090a61677c0b2.jpg

做一个简单的对话框

public class ConfirmDialog extends DialogFragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog dialog = new AlertDialog.Builder(getActivity())
.setTitle("提示")
.setMessage("确认要退出吗")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {

}
}).create();
return dialog;
}
}

显示它

ConfirmDialog dialog = new ConfirmDialog();
dialog.show(getSupportFragmentManager(), "dialogTag");

onCreateView

使用自定义视图做一个加载框。
这里有一个非常重要的地方,我出现了 Fragment already added 的问题,意思就是重复添加了,那么为什么会出现重复添加呢,因为我最初的代码是使用isAddedisVisibility进行判断,但是当快速执行的时候,这两个方法并不准确。

正确的做法有两种。

  • 添加事务时先进行移除
beginTransaction().remove(this).commit()
  • 使用变量进行判断,不能使用isAdded
private boolean isShowFragment = false;

@Override
public void show(@NonNull FragmentManager manager, @Nullable String tag) {
// 解决bug:Android java.lang.IllegalStateException: Fragment already added
if (this.isShowFragment) {
return;
}
this.isShowFragment = true;
super.show(manager, tag);
}

@Override
public void dismiss() {
super.dismiss();
this.isShowFragment = false;
}

// 避免有些手机兼容性问题,isShowFragment未变成false而导致无法二次打开
@Override
public void onDestroy() {
super.onDestroy();
this.isShowFragment = false;
}

最后直接放代码,封装好的Loading框

LoadingDialog 对话框 可以看到代码中对bug的处理:在每个add事务前增加一个remove事务,防止连续的add。

public class LoadingDialog extends DialogFragment
implements DialogInterface.OnKeyListener {
/**
* 加载框提示信息 设置默认
*/
private final String hintMsg = "加载中...";

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.MyDialog);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Dialog dialog = getDialog();
// 设置背景透明
if (dialog.getWindow() != null)
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
// 去掉标题
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setCanceledOnTouchOutside(false);
View loadingView = inflater.inflate(R.layout.dialog_loading, container);
TextView hintTextView = loadingView.findViewById(R.id.tv_ios_loading_dialog_hint);
hintTextView.setText(hintMsg);
//不响应返回键
dialog.setOnKeyListener(this);
return loadingView;
}

@Override
public void show(FragmentManager manager, String tag) {
try {
//在每个add事务前增加一个remove事务,防止连续的add
manager.beginTransaction().remove(this).commit();
super.show(manager, tag);
} catch (Exception e) {
//同一实例使用不同的tag会异常,这里捕获一下
e.printStackTrace();
}
}

@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
// return keyCode == KeyEvent.KEYCODE_BACK;
// 允许按back键取消Loading
return false;
}

}

代理管理类

public class GlobalDialogManager {

private LoadingDialog mLoadingDialog;

private GlobalDialogManager() {
init();
}

public static GlobalDialogManager getInstance() {
return SingletonHolder.INSTANCE;
}

private static class SingletonHolder {
private static final GlobalDialogManager INSTANCE = new GlobalDialogManager();
}

public void init() {
if (mLoadingDialog == null) {
mLoadingDialog = new LoadingDialog();
}
}

/**
* 展示加载框
*/
public synchronized void show(FragmentManager manager) {
if (manager != null && mLoadingDialog != null) {
mLoadingDialog.show(manager, "tag");
}
}

/**
* 隐藏加载框
*/
public synchronized void dismiss(FragmentManager manager) {
if (mLoadingDialog != null && !manager.isDestroyed()) {
mLoadingDialog.dismissAllowingStateLoss();
}
}
}

使用它

if (getContext() != null)
GlobalDialogManager.getInstance().show(((Activity) getContext()).getFragmentManager());

if (getContext() != null)
GlobalDialogManager.getInstance().dismiss(((Activity) getContext()).getFragmentManager());

这里判断getContext()很有必要,避免Activity消失了,getContext为空的bug。

背景不变暗设置一个style属性行啦。

<item name="android:backgroundDimEnabled">false</item><!--activity不变暗-->

0 个评论

要回复文章请先登录注册