bottomSheetDialogFragment 全屏

bottomSheetDialogFragment full screen

我想要实现的是类似 Instagram 应用内网络浏览器的功能,当您点击广告时使用:

我做了什么,是我使用了 WebView bottomSheetDialogFragment,我覆盖了 onCreateDialog 以获得全屏,如下所示:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    BottomSheetDialog bottomSheetDialog=(BottomSheetDialog)super.onCreateDialog(savedInstanceState);
    bottomSheetDialog.setOnShowListener(dialog -> {
        BottomSheetDialog dialogc = (BottomSheetDialog) dialog;
        FrameLayout bottomSheet =  dialogc .findViewById(android.support.design.R.id.design_bottom_sheet);
        BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
        //BottomSheetBehavior.from(bottomSheet).setSkipCollapsed(true);
        //BottomSheetBehavior.from(bottomSheet).setHideable(true);
    });
    return bottomSheetDialog;
}

这是我得到的结果:

我的问题是,如何获得全屏效果,或者如何实现类似instagram浏览器的效果?

ps:我先尝试了 chrome 自定义选项卡,但无法将其添加到对话框片段中。

谢谢。

您可以通过将 BottomSheetBehaviorpeekHeight 设置为等于 Resources.getSystem().getDisplayMetrics().heightPixels:

来实现
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    BottomSheetDialog bottomSheetDialog=(BottomSheetDialog)super.onCreateDialog(savedInstanceState);
    bottomSheetDialog.setOnShowListener(dialog -> {
        BottomSheetDialog dialogc = (BottomSheetDialog) dialog;
        // When using AndroidX the resource can be found at com.google.android.material.R.id.design_bottom_sheet
        FrameLayout bottomSheet =  dialogc.findViewById(android.support.design.R.id.design_bottom_sheet);

        BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
        bottomSheetBehavior.setPeekHeight(Resources.getSystem().getDisplayMetrics().heightPixels);
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    });
    return bottomSheetDialog;
}

在您的自定义 BottomSheetDialogFragment 中,您可以使用如下内容:

  @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) {
    Dialog dialog = super.onCreateDialog(savedInstanceState);
    dialog.setOnShowListener(new DialogInterface.OnShowListener() {
      @Override public void onShow(DialogInterface dialogInterface) {
        BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface;
        setupFullHeight(bottomSheetDialog);
      }
    });
    return  dialog;
  }


  private void setupFullHeight(BottomSheetDialog bottomSheetDialog) {
    FrameLayout bottomSheet = (FrameLayout) bottomSheetDialog.findViewById(R.id.design_bottom_sheet);
    BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
    ViewGroup.LayoutParams layoutParams = bottomSheet.getLayoutParams();

    int windowHeight = getWindowHeight();
    if (layoutParams != null) {
      layoutParams.height = windowHeight;
    }
    bottomSheet.setLayoutParams(layoutParams);
    behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
  }

  private int getWindowHeight() {
    // Calculate window height for fullscreen use
    DisplayMetrics displayMetrics = new DisplayMetrics();
    ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    return displayMetrics.heightPixels;
  }

建议的解决方案基于使用内部 id,因为它不是有意暴露的,它可能会在没有警告的情况下发生变化。

我的解决方案将布局高度设置为 FrameLayout,但以更抽象的方式设置,因此即使更改 ViewGroup 类型,它也不会中断。

override fun onStart() {
    super.onStart()
    val sheetContainer = requireView().parent as? ViewGroup ?: return
    sheetContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
}

如果您查看 BottomSheetDialogprivate View wrapInBottomSheet 方法,您会发现这恰好保证了 sheet 行为。一些额外的调试,让我知道片段 View 是直接来自 FrameLayout 每个人都通过 id 找到的 child。

这样就不用依赖ID了。我正在使用 onStart 因为它是在片段准备好进行交互时定义的,所以一切都应该准备就绪。

如果您从一开始就需要全高(最有可能)

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return BottomSheetDialog(requireContext(), theme).apply {
        behavior.state = BottomSheetBehavior.STATE_EXPANDED
        behavior.peekHeight = //your harcoded or dimen height
    }
}

抱歉回答晚了,但在您的自定义 BottomSheetDialogFragment 中,您可以将 match_parent 设置为底部 sheet 视图的布局参数,如下所示:

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = BottomSheetDialog(requireContext(), theme)
    dialog.setOnShowListener {

        val bottomSheetDialog = it as BottomSheetDialog
        val parentLayout =
            bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
        parentLayout?.let { it ->
            val behaviour = BottomSheetBehavior.from(it)
            setupFullHeight(it)
            behaviour.state = BottomSheetBehavior.STATE_EXPANDED
        }
    }
    return dialog
}

private fun setupFullHeight(bottomSheet: View) {
    val layoutParams = bottomSheet.layoutParams
    layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT
    bottomSheet.layoutParams = layoutParams
}

将高度设置为 match_parent 有助于将对话框绘制在导航栏的插入部分上方

最好的方法是通过 XML

通过覆盖其属性自定义默认 BottomSheet,例如 styles.xml

<style name="Widget.MyApp.BottomSheet.Modal" parent="Widget.MaterialComponents.BottomSheet.Modal">
    <item name="behavior_skipCollapsed">true</item>
    <item name="behavior_fitToContents">true</item>
    <item name="behavior_peekHeight">1000dp</item> // yep, that helped to skip collapsed state at initial
    <item name="behavior_hideable">true</item>
</style>

进入 Widget.MaterialComponents.BottomSheet.Modal 看看您可以修改哪些设置。

然后创建继承自 Theme.Design.BottomSheetDialog 的自定义主题,并设置您要用自己的样式覆盖底部 sheet 的样式。也可以放在styles.xml

<style name="Theme.MyApp.BottomSheetDialog" parent="Theme.Design.BottomSheetDialog">
    <item name="bottomSheetStyle">@style/Widget.MyApp.BottomSheet.Modal</item>
</style>

最后,在 Activity 的主题或应用程序的主题中为底部 sheet 对话框定义新创建的主题,该主题位于 themes.xml(希望您遵循 Google关于包装风格和主题的建议)

<style name="Base.Theme.MyApp" parent="Base.Theme.Root">
    ... too many other things
    <item name="bottomSheetDialogTheme">@style/Theme.MyApp.BottomSheetDialog</item>

以下是您在 kotlin 中的做法,

val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener {
    val bottomSheetDialog = it as BottomSheetDialog
    val parentLayout = bottomSheetDialog.findViewById<View>(
        com.google.android.material.R.id.design_bottom_sheet
    )
    parentLayout?.let { bottomSheet ->
        val behaviour = BottomSheetBehavior.from(bottomSheet)
        val layoutParams = bottomSheet.layoutParams
        layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT
        bottomSheet.layoutParams = layoutParams
        behaviour.state = BottomSheetBehavior.STATE_EXPANDED
    }
}
return dialog

在我的案例中使用 kotlin

   private fun exerciseDialog() {
                val dialogBinding: DialogExerciseBinding = DataBindingUtil.inflate(
                    LayoutInflater.from(requireContext()), R.layout.dialog_exercise, null, false
                )
                val mDialog = BottomSheetDialog(requireContext())
                mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
                mDialog.setContentView(dialogBinding.root)
                mDialog.setCanceledOnTouchOutside(true)
        
                val parentLayout =
                    mDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
                parentLayout?.let { it_ ->
                    val behaviour = BottomSheetBehavior.from(it_)
                    setupFullHeight(it_)
                    behaviour.state = BottomSheetBehavior.STATE_EXPANDED
                }
        
                mDialog.show()
        
            }
        
            private fun setupFullHeight(bottomSheet: View) {
                val layoutParams = bottomSheet.layoutParams
                layoutParams.height = (getScreenHeight(requireActivity()) * .90).toInt()
                bottomSheet.layoutParams = layoutParams
            }

  fun getScreenHeight(activity: Activity): Int {
        val outMetrics = DisplayMetrics()

        return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
            val display = activity.display
            display?.getRealMetrics(outMetrics)
            outMetrics.heightPixels
        } else {
            @Suppress("DEPRECATION")
            val display = activity.windowManager.defaultDisplay
            @Suppress("DEPRECATION")
            display.getMetrics(outMetrics)

            outMetrics.heightPixels
        }
    }

您可以将对话框的状态更改为 STATE_EXPANDED:

BottomSheetDialog dialog = (BottomSheetDialog) getDialog();
dialog.getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED);

科特林:

val dialog = dialog as BottomSheetDialog
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED

官方的漂亮回答Material-Componentsgithub

您所要做的就是施展魔法 - 将其添加到您的应用中 theme/style

<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="enableEdgeToEdge">true</item>

全屏模式

On API 21 and above the BottomSheet will be rendered fullscreen (edge to edge) if the navigationBar is transparent, and enableEdgeToEdge is true. It can automatically add insets if any of paddingBottomSystemWindowInsets, paddingLeftSystemWindowInsets, paddingRightSystemWindowInsets, or paddingTopSystemWindowInsets are set to true in the style, either by updating the style passed to the constructor, or by updating the default style specified by the bottomSheetDialogTheme attribute in your theme.

BottomSheetDialog will also add padding to the top when the BottomSheet slides under the status bar to prevent content from being drawn underneath it.

我是这样用的:

  • 在根布局中添加带有“android:layout_height="match_parent”的视图
  • 显示对话框时(或在此之前)使用“isFitToContents = false”和“state = BottomSheetBehavior.STATE_EXPANDED”更新 BottomSheetBehavior。

代码:

override fun onShow(dialog: DialogInterface?) {
    BottomSheetBehavior.from(binding?.root?.parent as View).apply {
        isFitToContents = false
        state = BottomSheetBehavior.STATE_EXPANDED
    }
}

Xml 文件:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/fragment_cities_root_background">

        <View
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <!-- other views -->

    </androidx.constraintlayout.widget.ConstraintLayout >

只需添加以下代码行

bottomSheetDialog.getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED);

布局应在 NestedScrollView

在 BottomSheetDialogFragment 的 onViewCreated 中使用这个

dialog.setOnShowListener { dialog ->
    val d = dialog as BottomSheetDialog
    val bottomSheet = d.findViewById<View>(R.id.design_bottom_sheet) as FrameLayout
    val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
    bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}