简体   繁体   中英

bottomSheetDialogFragment full screen

What I'm trying to achieve is something like Instagram in-app web browser, used when you click an ad:

Instagram 网络浏览器

what I did, is I used a WebView bottomSheetDialogFragment, and I override onCreateDialog to get the full screen like this:

@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;
}

here is the result I get:

网页浏览器

my question is, how can I get the full screen effect, or how can achieve something like instagram browser?

ps: I tried first chrome custom tabs, but I couldn't add it inside dialog fragment.

Thank you.

In your custom BottomSheetDialogFragment you can use something like:

  @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;
  }

Sorry for the late answer but in your custom BottomSheetDialogFragment you can set match_parent to layout params of bottom sheet view like this:

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
}

Setting height to match_parent helps your dialog to be drawn above the inset of Navigation bar

You can do it by setting peekHeight of BottomSheetBehavior to be equal to 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;
}

Proposed solutions are based on using an internal id , since it is not on purpose exposed it could change without warning.

My solution set the layout height to the FrameLayout but in a more abstract way, so if it changes it shouldn't break, even if the ViewGroup type change.

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

If you take a look at the BottomSheetDialog the private View wrapInBottomSheet method you will see this happens to guarantee the sheet behavior. Some extra debugging, allowed me to figure the fragment View is the direct child from the FrameLayout everybody is finding by id.

In this way, you don't need to rely on the ID. I'm using onStart because is defined at the point when the fragment is ready for interactions, so all should be ready.

And if you need it full height from the start (most probably)

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

The best way to to this is through XML

Customize default BottomSheet by overriding it's attributes, for example in 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>

Go inside Widget.MaterialComponents.BottomSheet.Modal to see what settings you can modify.

Then create custom theme inherited from Theme.Design.BottomSheetDialog and set that you want to override bottom sheet's style with you own. Also can be placed in styles.xml

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

And the last, define your new created theme for bottom sheet dialog in your Activity's theme or Application's theme, which placed in themes.xml(hope you follow Google's recommendations about packaging styles&themes)

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

Here's how you do it in 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

You can do it by changing the state of your dialog to STATE_EXPANDED :

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

Kotlin:

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

I used such way:

  • add a view with "android:layout_height="match_parent" into root layout
  • when dialog is shown (or before this) update of BottomSheetBehavior with "isFitToContents = false" and "state = BottomSheetBehavior.STATE_EXPANDED".

Code:

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

Xml file:

    <?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 >

just add below line of code

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

and layout should be inside NestedScrollView

try this in your custom BottomSheetDialogFragment:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setupFullHeight()

    }

    private fun setupFullHeight() {
        val bottomSheetDialog = dialog as BottomSheetDialog
        val behavior = bottomSheetDialog.behavior
        behavior.state = BottomSheetBehavior.STATE_EXPANDED
    }

In my Case using 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
        }
    }

A pretty answer from official Material-Components github

All you have to do is some magic - add this to you app theme/style

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

Fullscreen mode

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.

Use this in onViewCreated of BottomSheetDialogFragment

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
}

this worked perfectly for me:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    (dialog as? BottomSheetDialog)?.behavior?.state = 
    BottomSheetBehavior.STATE_EXPANDED
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM