“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 use
isAdded
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