preface

In Android, there are two kinds of dialogs to create, one is Dialog, the other is today’s topic, DialogFragmet officially recommended, about the use of Dialog will not be described, today mainly introduces the use of DialogFragment and some matters needing attention, mainly have the following points.

  • DialogFragmeng’s advantages over Dialog.

  • Use of DialogFrargment.

  • DialogFragment simple source code analysis.

  • DialogFragment needs to pay attention to some small details.

First, the advantages of DialogFragment

  • Before DialogFragment, Dialog was generally used to create Dialog boxes, and from the point of view of code writing, Dialog is actually simpler to use, but Google recommends DialogFragment as much as possible, isn’t it strange? In fact, the reason is very simple, DialogFragment has very good characteristics that Dialog does not have, which can make it more reusable (reduce coupling) and better convenience (better handle the situation of screen flipping).

Second, the use of DialogFragment

  • Method 1: Override the onCreateDialog method of DialogFragment

  • Method 2: Override the onCreateView method of DialogFragment

1. Specific use of Method 1

  • Override onCreateDialog in DialogFragment to return a Dialog.
    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Logger.d(TAG, "onCreateDialog");
        AppCompatDialog appCompatDialog = new AppCompatDialog(requireActivity());
        TextView textView = new TextView(requireActivity());
        textView.setText("Using DialogFragment via onCreateDialog");
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 28);
        appCompatDialog.setContentView(textView);
        return appCompatDialog;
    }
Copy the code
  • Use DialogFragment in your Activity.
    private void showOnCreateDialogFragment(a){
        DialogFragment dialogFragment = new DialogFragment();
        dialogFragment.show(getSupportFragmentManager(), "tag");
    }
Copy the code

2. Use method 2

  • Overwrite the onCreateView method in DialogFragment. The View created by this method will be used as the content layout of the Dialog in the same way that method 1 was used in the Activity.
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Logger.d(TAG, "onCreateView");
        TextView textView = new TextView(requireActivity());
        textView.setText("Use DialogFragment with onCreateView");
        textView.setGravity(Gravity.CENTER);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 28);
        return textView;
    }
Copy the code

DialogFragment = onStar, DialogFragment = onStar, DialogFragment = onStar < div style = “box-type: break-word; color: RGB (51, 51, 51); line-height: 22px; font-size: 14px! Important; word-break: break-all;”

2019-03-10 14:19:10.971 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onAttach
2019-03-10 14:19:10.971 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onCreate
2019-03-10 14:19:10.972 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onCreateDialog
2019-03-10 14:19:10.994 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onActivityCreated
2019-03-10 14:19:11.186 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onStart
2019-03-10 14:19:11.186 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onResume
Copy the code
  • From the callback lifecycle method printed above, we can set the Dialog property in the onStart method as follows:
    @Override
    public void onStart(a) {
        super.onStart();
        Dialog dialog = getDialog();
        Window window = dialog.getWindow();
        if (window == null) {
            return;
        }
        WindowManager.LayoutParams attributes = window.getAttributes();
        // Set the height of the Dialog window
        attributes.height = WindowManager.LayoutParams.MATCH_PARENT;
        // Set the width of Dialog window
        attributes.width = WindowManager.LayoutParams.MATCH_PARENT;
        // Sets the center orientation of the Dialog
        attributes.gravity = Gravity.CENTER;
        // Set the transparency of the background when the Dialog pops up
        attributes.dimAmount = 0.6 f;
        // Set the spacing of the Dialog horizontally
        attributes.horizontalMargin = 0f;
        // Set the spacing of the Dialog vertically
        attributes.verticalMargin = 0f;
        // Set the X axis of the Dialog display, the X axis offset of the screen
        attributes.x = 0;
        // Set the Y axis of the Dialog display, the offset from the screen Y axis
        attributes.y = 0;
        // Set the transparency of the Dialog
        attributes.alpha = 0f;
        // Sets the animation when the Dialog displays and disappears
        attributes.windowAnimations = 0;
        window.setAttributes(attributes);
        Logger.d(TAG, "onStart");
    }

Copy the code

DialogFragmeng source code analysis

1. The following post part of the source code of DialogFragment, and a brief analysis.

public class DialogFragment extends Fragment implements OnCancelListener.OnDismissListener {
    boolean mViewDestroyed;
    boolean mDismissed;
    boolean mShownByMe;

    public DialogFragment(a) {}// The standard way to display fragments, nothing to say
    public void show(FragmentManager manager, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commit();
    }
    
    // Pass in a transaction manager, add the fragment to the transaction manager, and return the rollback stack ID
    // The returned mBackStackId will be used in the following article
    public int show(FragmentTransaction transaction, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        transaction.add(this, tag);
        this.mViewDestroyed = false;
        this.mBackStackId = transaction.commit();
        return this.mBackStackId;
    }
    
    // The fragment method is a modified version of the first show method. After using this method, the fragment method will be executed immediately
    public void showNow(FragmentManager manager, String tag) {
        this.mDismissed = false;
        this.mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commitNow();
    }

    // setOndismissListener is set in onActivityCreate, which seamlessly moves dialog into dismiss, and does not move dialogfragment out of the stack
    public void dismiss(a) {
        this.dismissInternal(false);
    }
    
    // Enhanced version of dismiss, first disappear dialog, and remove dialogFragment from stack
    public void dismissAllowingStateLoss(a) {
        this.dismissInternal(true);
    }

    // Display to determine whether dialog has disappeared, if it has disappeared, do nothing
    //1. If the dialog instance is not empty, call the Dialog's dismiss method first to hide the dialog
    Public int show(FragmentTransaction transaction, String tag)
    // method displays a dialogFragment, which will be removed from the stack based on the previously returned mBackStackId
    //3. If not, enable transaction to remove dialogFragment from stack, which is differentiated according to the passed allowStateLoss
    // The method to commit the transaction
    void dismissInternal(boolean allowStateLoss) {
        if (!this.mDismissed) {
            this.mDismissed = true;
            this.mShownByMe = false;
            if (this.mDialog ! =null) {
                this.mDialog.dismiss();
            }

            this.mViewDestroyed = true;
            if (this.mBackStackId >= 0) {
                this.getFragmentManager().popBackStack(this.mBackStackId, 1);
                this.mBackStackId = -1;
            } else {
                FragmentTransaction ft = this.getFragmentManager().beginTransaction();
                ft.remove(this);
                if (allowStateLoss) {
                    ft.commitAllowingStateLoss();
                } else{ ft.commit(); }}}}// If a subclass overrides this method, it uses your custom dialog
    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new Dialog(this.getActivity(), this.getTheme());
    }
    
    // Dialog sets the callback after onDismissListener; the method in that callback is not called if the dialog disappears normally
    // Because the mViewDestroyed variable is set to true in the onStar method of the DialogFragment, the Dialog display is set to the life cycle callback shown by the call, which we have printed.
    public void onDismiss(DialogInterface dialog) {
        if (!this.mViewDestroyed) {
            this.dismissInternal(true); }}If the View is not empty, the View will be used as the content layout of the Dialog, so if you override both the onCreateDialog and onCreateView methods, The View in onCreateView is used as the content layout first, and then some listening Settings are made
    // Set dialog to clickable
    // The dialog's disappear listener is configured so that the dimiss method is called back when it disappears
    // Set the dialog's cancel listener, onCancelListener, to call the onCancel method in the context of cancellation
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (this.mShowsDialog) {
            View view = this.getView();
            if(view ! =null) {
                if(view.getParent() ! =null) {
                    throw new IllegalStateException("DialogFragment can not be attached to a container view");
                }

                this.mDialog.setContentView(view);
            }

            Activity activity = this.getActivity();
            if(activity ! =null) {
                this.mDialog.setOwnerActivity(activity);
            }

            this.mDialog.setCancelable(this.mCancelable);
            this.mDialog.setOnCancelListener(this);
            this.mDialog.setOnDismissListener(this);
            if(savedInstanceState ! =null) {
                Bundle dialogState = savedInstanceState.getBundle("android:savedDialogState");
                if(dialogState ! =null) {
                    this.mDialog.onRestoreInstanceState(dialogState);
                }
            }

        }
    }
}

Copy the code

2. Source code analysis and summary

  • You can override onCreateDialog or onCreateView to create a DialogFragment view, but onCretaeView has a higher priority than onCreateDilaog, so there is no special need. Create the Dialog view by overriding onCreateView.

  • From the beginning we printed a series of DialogFragment lifecycle methods from the creation of the view to the show callback. We learned that setting dialog properties can be done in the onStar callback in the DialogFragment.

  • The show() method passes in a fragmentManager and a tag, showNow method, passes in a fragmentManger and a tag, show() method, You pass in a transaction and a tag, and return the ID of the callback in that transaction. When you call the dismissInternal() method, the DialogFragment is removed from the stack based on the type of DialogFragment that is displayed.

  • If you override the onCrateView method and return a non-empty instance, it will act as the content view of the Dialog. The dialog is then set to cancel, disappear listening, and cancel listening

  • DialogFragment will be removed from the stack when DialogFragment disappears. DialogFragment will be removed from the stack when DialogFragment disappears. DialogFragment will be removed from the stack when dialog disappears. You need to call the dismissInternal() method manually

4. Attention to details

  • You can override onCreateDialog and onCreateView to set the DialogFragment view, but onCraeteView sets the view to a higher priority than onCreateDialog.

  • Set Dialog properties after the onCreateDialog callback, otherwise getDialog will return null. It is recommended to set Dialog properties in the onStar method.

  • DialogFrament can be displayed only once for a DialogFragment instance. If DialogFrament is displayed multiple times, the following exception will be displayed

    Process: org.lym.sourcecodeparse, PID: 27108
    java.lang.IllegalStateException: Fragment already added: DialogFragment{8262d74 #0 tag}
        at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1893)
        at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:760)
        at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2595)
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2382)
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2337)
Copy the code
  • After normal Dismiss, the DialogFragment will not be removed from the current stack. Instead, the DialogFragment will be removed from the stack when onDestroyView() in DialogFragment calls back. So if you need to hide and display a DialogFragment frequently in your Activity, you need to manually call the dismissAllowingStateLoss() method in your dismiss. In addition, you cannot use a DialogFragment instance when you show again.

  • DialogFragment does not provide a listener for the disappearance of Dialog, but we know from source code analysis, DialogFragment in onActivityCreate already allows onDismissListener to be configured, so you need to dismiss the Dialog by overriding the onDismiss method. This allows you to seamlessly dismiss the onListener listener from your parent; this allows you to seamlessly dismiss the listener after the onActivityCreate method is called.

//mListener provides a callback for external use
 @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        mListener.onDismiss(dialog);
        Logger.d(TAG, "onDismiss");
    }
    
    // the overlay setOnDismissListener must occur after the parent class's onActivityCreate
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if(getDialog() ! =null && null! = mListener) { getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
                @Override
                public void onDismiss(DialogInterface dialog) {
                    ToastUtils.showToast("OnDismiss Listener after overwriting"); }}); } Logger.d(TAG,"onActivityCreated");
    }
Copy the code

Conclusion: The above is the content of the full text, is in the use of DialogFragment encountered some pits and some understanding after learning, I hope to help you.