DialogFragment

Android 中 Dialog 并没方法感知生命周期,但 Frg 可以感知,所以将 Diglog 与 Frg 结合后生成 DialogFragment,它供给了可以感知生命周期的 Dialog。其他 DialogFragment 也继承 Fragment,所以也可以当作一般的 Fragment 运用。

因为 DialogFragment 可以感知生命周期,并且还处理了 Dialog 状况的保存与恢复,所以 DialogFragment 应多用

原理

DialogFragment 中的 Dialog 是在 onGetLayoutInflater 中创立的,该方法会先于 onCreateView() 调用,但后于 onCreate()。

第一个问题:DialogFragment 当作 Dialog 运用时为什么不会闪现到 Activity 布局中,而是以 Dialog 形式呈现。将 DialogFragment 当作 Dialog 用时需求调用其 show 方法,其内部也是运用了 FragmentTransaction,只不过依托的 containerViewId 是 0,因此无法添加到某个 View 上,也就不会在 Activity 布局中闪现。

// show() 方法节选
FragmentManager manager = ....
FragmentTransaction ft = manager.beginTransaction();
ft.setReorderingAllowed(true);
ft.add(this, tag);
ft.commit();
// FragmentTransaction::add() 方法
public FragmentTransaction add(@NonNull Fragment fragment, @Nullable String tag)  {
    // 首要第一个参数是 0,这就导致调用 show 方法时不会闪现到具体界面
    doAddOp(0, fragment, tag, OP_ADD);
    return this;
}
// 将 Frg 当作一般 Frg 运用时会调用该方法
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment) {
    // 此刻 containerViewId 就不是 0,所以 Fragment 中的 view 会闪现到界面上
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}

通过 show() 方法将其时 Fragment 绑定到了某一个 Activity,当 Frg 感知到生命周期发生变化时就会将 Dialog 闪现或躲藏,并且在 onSaveInstanceState 等方法也处理了状况的保存与闪现。

// onResume() 回调中会闪现 dialog
// onStop() 回调中躲藏 dialog
// onDestroyView() 中会毁掉 Dialog
public void onStop() {
    super.onStop();
    if (mDialog != null) {
        mDialog.hide();
    }
}

第二个问题:onCreateView() 回来的 View 有什么用onCreateView() 回来的 View 就是 Dialog 要闪现的内容。在 DialogFragment::onAttach() 中会添加一个监听

// mObserver 中通过 requireView() 拿到 onCreateView() 的回来值
// 一起调用 Dialog::setContentView() 将回来值设置成 dialog 闪现的内容
// mObserver 节选
if (lifecycleOwner != null && mShowsDialog) {
    // 拿到 onCreateView 回来值
    View view = requireView();
    if (view.getParent() != null) {
        throw new IllegalStateException(
                "DialogFragment can not be attached to a container view");
    }
    if (mDialog != null) {
        mDialog.setContentView(view);
    }
}
// onAttach() 节选
getViewLifecycleOwnerLiveData().observeForever(mObserver);

第三个问题:上面 mObserver 看出如果不重写 onCreateView(),requireView() 应该会报错的,为啥运用时不会出问题。这个首要在 Fragment::performCreateView() 中

mViewLifecycleOwner = new FragmentViewLifecycleOwner(this, getViewModelStore());
// 调用 onCreateView() 默许回来 null
mView = onCreateView(inflater, container, savedInstanceState);
if (mView != null) {
   // 只有 mView 不为 null 时才触发 liveData 更新,上面的 mObserver 才会收到告诉
    // Then inform any Observers of the new LifecycleOwner
    mViewLifecycleOwnerLiveData.setValue(mViewLifecycleOwner);
} else {
    mViewLifecycleOwner = null;
}

BottomSheetDialogFragment

继承于 DialogFragment,只不过它回来的 Dialog 是 BottomSheetDialog。从上面可以看出 onCreateView() 的回来值最终会传递给 Dialog::setContentView() 中。BottomSheetDialog::setContentView() 会调用 wrapInBottomSheet(),最核心的两句就是:

// 它会初始化一个 View,该 View 就是 Dialog 要闪现的 View
ensureContainerAndBehavior();
// view 就是我们传入 setContentView 中的 view
// bottomSheet 就是上面 View 的一个子 View
bottomSheet.addView(view);
// 生成 Dialog 要闪现的 View 
private FrameLayout ensureContainerAndBehavior() {
  if (container == null) {
    container =
        (FrameLayout) View.inflate(getContext(), R.layout.design_bottom_sheet_dialog, null);
    coordinator = (CoordinatorLayout) container.findViewById(R.id.coordinator);
    // 检查上面的布局,可以发现 bottomSheet 是 CoordinatorLayout 的子类
    // 其 behavior 是 com.google.android.material.bottomsheet.BottomSheetBehavior
    bottomSheet = (FrameLayout) container.findViewById(R.id.design_bottom_sheet);
    behavior = BottomSheetBehavior.from(bottomSheet);
    behavior.addBottomSheetCallback(bottomSheetCallback);
    behavior.setHideable(cancelable);
  }
  return container;
}

总之,BottomSheetDialogFragment 会将 onCreateView() 的回来成果包裹在 CoordinatorLayout 中,然后实现 bottom_sheet 效果