“This is the fifth day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

Use DialogFragment instead of Dialog

Well, I have been using a Dialog tool class for a long time, but I found a bug today. I tried to search and found that everyone already uses DialogFragment, which is officially recommended, and suddenly realized that I have been outdated for so long. Try it now.

What is DialogFragment

DialogFragment from its source code, it inherits a Fragment, is actually a relatively special Fragment. So how does it differ from a regular Dialog, why does Google recommend it, and what advantages does it have over a regular Dialog?

Describe your feelings after using it:

  • Its lifecycle is clear, making it easy to write complex logic
  • It is bound to the Activity’s life cycle. When the Activity disappears, the DialogFragment also disappears.
  • It makes it easy to control the layout of popovers.

The conclusion is that DialogFragment can better manage the display and disappearance of dialog, as well as some state preservation problems when the screen rotates.

DialogFragment on pit

Even though it has a lot of advantages, it can still have a lot of holes when used incorrectly. I had a lot of weird problems. Such as

  • Fragment Already Added Exception
  • Fast display disappear, cannot disappear exceptions

Of course, we may also have the following requirements:

  • Set the size of the dialog box
  • Set the background of the pop-up dialog box to gray or transparent

Let’s implement them one by one.

How to implement DialogFragment

Focus on two approaches.

  • OnCreateDialog Create a Dialog to use
  • OnCreateView specifies a custom Dialog interface

onCreateDialog

Create a simple dialog box

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(" prompt ").setMessage(" Are you sure you want to exit? ").setpositiveButton (" Ok ", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, Int which) {}}). SetNegativeButton (" cancel ", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }).create(); return dialog; }}Copy the code

Show it

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

onCreateView

Make a load box with custom view. There is a very important point here, I have the problem of Fragment already added, which means it has been added repeatedly. Then why is it added repeatedly? My original code uses isAdded and isVisibility to judge. These two methods are not accurate.

There are two ways to do this.

  • Remove transactions before adding them
beginTransaction().remove(this).commit()
Copy the code
  • Use variables to determine whether to useisAdded
private boolean isShowFragment = false; @override public void show(@nonnull FragmentManager, @nullable String tag) { 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; } @override public void onDestroy() {super.ondestroy (); this.isShowFragment = false; }Copy the code

Finally, put the code directly and encapsulate the Loading box

The LoadingDialog dialog box shows how the bug is handled in the code: add a remove transaction before each add transaction to prevent successive add.

Public class LoadingDialog extends DialogFragment implements DialogInterface. OnKeyListener {/ * * * load box warning information to set the default * / Private Final String hintMsg = "Loading..." ; @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(); // Set background transparency if (dialog.getwindow ()! = null) dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); / / remove the title 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); // Does not respond to the return key dialog.setonKeyListener (this); return loadingView; } @override public void show(FragmentManager manager, String tag) {try { Prevent sequential add Manager.beginTransaction ().remove(this).commit(); super.show(manager, tag); } catch (Exception e) {// The same instance with a different tag will be an Exception, e.printStackTrace(); } } @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { // return keyCode == KeyEvent.KEYCODE_BACK; // Can press the back key to cancel Loading return false; }}Copy the code

Agent management class

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) {if (manager! = null && mLoadingDialog ! = null) { mLoadingDialog.show(manager, "tag"); }} public synchronized void dismiss(FragmentManager) {if (mLoadingDialog! = null && ! manager.isDestroyed()) { mLoadingDialog.dismissAllowingStateLoss(); }}}Copy the code

Use it

if (getContext() ! = null) GlobalDialogManager.getInstance().show(((Activity) getContext()).getFragmentManager()); if (getContext() ! = null) GlobalDialogManager.getInstance().dismiss(((Activity) getContext()).getFragmentManager());Copy the code

It is necessary to determine getContext() to avoid the bug where the Activity disappears and getContext is empty.

Set a style property for the background to remain dark.

<item name="android:backgroundDimEnabled">false</item><! -- Activity stays dark -->Copy the code