注册

DialogFragment 与 BottomSheetDialogFragment

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 效果


作者:鱼洗竹
链接:https://juejin.cn/post/7226287155668549690
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册