DialogFragment与Dialog有什么区别

843 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情

前言

在开始学习 Android 的时候,制作对话框,无疑都是直接使用 Dialog,然后对其进行自定义,而后面推出 DialogFragment 后,更多人都开始使用 DialogFragment 了,那么,DialogFragment 与 Dialog 到底有什么区别?

首先,我们来分析下 DialogFragment。

DialogFragment 生命周期

public class DialogFragment extends Fragment
        implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {

可以看得出,DialogFragment 是继承于 Fragment 的,也就是说 DialogFragment 的执行过程其实就是 Fragment 生命周期的执行过程。

那么我们来初步回顾下 Fragment 的生命周期。

  • onAttach
  • onCreate
  • onCreateView
  • onActivityCreated
  • onStart
  • onResume
  • onPause
  • onStop
  • onDestroyView
  • onDestroy
  • onDetach

至于这些生命周期的含义就不说明了,网上有很多文章,这个也不是本文的重点。

我们从上往下进行排查:

onAttach

    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        getViewLifecycleOwnerLiveData().observeForever(mObserver);
        if (!mShownByMe) {
            mDismissed = false;
        }
    }
    private Observer<LifecycleOwner> mObserver = new Observer<LifecycleOwner>() {
        public void onChanged(LifecycleOwner lifecycleOwner) {
            if (lifecycleOwner != null && mShowsDialog) {
                View view = requireView();
				···
                if (mDialog != null) {
					···
                    mDialog.setContentView(view);
                }
            }
        }
    };

其实就是对 Fragment 进行生命周期的监听,当 Dialog 在展示的时候,更改 Dialog 的视图。

onCreate

    public void onCreate(@Nullable Bundle savedInstanceState) {
		···
        mHandler = new Handler();

        mShowsDialog = mContainerId == 0;

        if (savedInstanceState != null) {
            mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
            mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
            mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
            mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
            mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
        }
    }

主要是初始化,并且还原之前存储的数据。

onCreateView

没有实现。

onActivityCreated

没有实现。

onStart

    public void onStart() {
        super.onStart();

        if (mDialog != null) {
            mViewDestroyed = false;
            mDialog.show();
            // Only after we show does the dialog window actually return a decor view.
            View decorView = mDialog.getWindow().getDecorView();
            ViewTreeLifecycleOwner.set(decorView, this);
            ViewTreeViewModelStoreOwner.set(decorView, this);
            ViewTreeSavedStateRegistryOwner.set(decorView, this);
        }
    }

主要是将 Dialog 展示出来。

onResume

没有实现。

onPause

没有实现。

onStop

    public void onStop() {
        super.onStop();
        if (mDialog != null) {
            mDialog.hide();
        }
    }

将 Dialog 进行关闭。

onDestroyView

    public void onDestroyView() {
        super.onDestroyView();
        if (mDialog != null) {
            mViewDestroyed = true;
            mDialog.setOnDismissListener(null);
            mDialog.dismiss();
            if (!mDismissed) {
                onDismiss(mDialog);
            }
            mDialog = null;
            mDialogCreated = false;
        }
    }

再次确认关闭 Dialog,并且对数据进行清空。

onDestroy

没有实现。

onDetach

没有实现。

从上面的生命周期的分析,其实我们可以看出,DialogFragment 其实是利用 Fragment 的生命周期对 Dialog 进行管控的,方便 Dialog 的创建到销毁的流程管控。

Dialog 和 Fragment 的绑定关系

可能有些同学就有疑问了,通过上面的讲解,并没有实际性看出 Dialog 和 Fragment 的绑定关系啊,为什么我给 Fragment 设置 UI,会展示到 Dialog 中?

这是因为我们在创建 Fragment 的时候,总会重载其 onCreateView 方法,在里面进行布局的设置,而在 onAttach 中,我们讲过监听了 Fragment 的生命周期,而 Fragment 的生命周期改变的时候,若 Dialog 在 show 的状态,就会把 Fragment 的布局效果设置给 Dialog,这就完成了 Dialog 和 Fragment 的绑定。

Dialog 的创建

在上面的内容中,其实我们漏了很重要的一点,那就是 Dialog 的创建,也就是 onCreateDialog 方法:

    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
            Log.d(TAG, "onCreateDialog called for DialogFragment " + this);
        }
        return new Dialog(requireContext(), getTheme());
    }

再往上追溯,其实就是到了 onGetLayoutInflater 中:

    public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {
        LayoutInflater layoutInflater = super.onGetLayoutInflater(savedInstanceState);
        if (!mShowsDialog || mCreatingDialog) {
			···
            return layoutInflater;
        }

        prepareDialog(savedInstanceState);
		···
	
        if (mDialog != null) {
            layoutInflater = layoutInflater.cloneInContext(mDialog.getContext());
        }
        return layoutInflater;
    }

到这里我们就结束了,再往上,我们就要分析 Fragment 整套的布局流程了,我们只要知道,若想要自定义 DialogFragment 中的 Dialog,我们可以通过重载 onCreateDialog 的方式来解决这个问题。