Abdel
Abdel

Reputation: 1095

bottomSheetDialogFragment full screen

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

Instagram web browser

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 :

web browser

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.

Upvotes: 74

Views: 111814

Answers (17)

Ranjit
Ranjit

Reputation: 11

MY WORKING CODE

public class ProfileSheet extends BottomSheetDialogFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for the profile sheet
        View view = inflater.inflate(R.layout.my_profile_sheet, container, true);

        return view;
    }
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        View layout = view.findViewById(R.id.some_layout_id);
        if (layout != null) {
            layout.setMinimumHeight(Resources.getSystem().getDisplayMetrics().heightPixels);
        }

        // Get the BottomSheetBehavior associated with the parent view
        BottomSheetBehavior<View> bottomSheetBehavior = BottomSheetBehavior.from((View) view.getParent());

        // Set the BottomSheet to the expanded state
        bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);

        // Assuming 'layout' is already initialized somewhere in your code
        if (layout != null) {
            // Set the minimum height of the layout to the screen height
            layout.setMinimumHeight(Resources.getSystem().getDisplayMetrics().heightPixels);
        } else {
            throw new IllegalStateException("Layout cannot be null");
        }
    }


}

Upvotes: 1

onwuegbuchi eric
onwuegbuchi eric

Reputation: 9

@Override
public void onViewCreated (@NonNull View view, @Nullable Bundle savedInstanceState) {

super.onViewCreated (view, savedInstanceState);

BottomSheetBehavior<View> bottomSheetBehavior BottomSheetBehavior.from ((View) view.getParent());

bottomSheetBehavior.setState(BottomSheetBehavior.STATE EXPANDED);

assert layout!=null;

layout.setMinimum Height (Resources.getSystem().getDisplayMetrics().heightPixels);

}

This worked for me , 4lines of code and it's done when the bottom sheet fragment is created .

Upvotes: -1

fraggjkee
fraggjkee

Reputation: 3614

Full screen + customizable rounded corners: enter image description here

Define the following extension -

fun Dialog.setFullScreen(
    @Px cornerRadius: Int = <some value>,
    skipCollapsed: Boolean = true
) {
    check(this is BottomSheetDialog) {
        "Dialog must be a BottomSheetBottomSheetDialog."
    }

    lifecycleScope.launch {
        whenStarted {
            val bottomSheetLayout = findViewById<ViewGroup>(com.google.android.material.R.id.design_bottom_sheet)  ?: return@whenStarted
            with(bottomSheetLayout) {
                updateLayoutParams {
                    height = ViewGroup.LayoutParams.MATCH_PARENT
                }
                clipToOutline = true
                outlineProvider = object : ViewOutlineProvider() {
                    override fun getOutline(view: View, outline: Outline) {
                        outline.setRoundRect(
                            0,
                            0,
                            view.width,
                            view.height + cornerRadius,
                            cornerRadius.toFloat()
                        )
                    }
                }
            }
            behavior.state = BottomSheetBehavior.STATE_EXPANDED
            behavior.skipCollapsed = skipCollapsed
        }
    }
}

Then just do this from your BottomSheetDialogFragment:

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return super.onCreateDialog(savedInstanceState).apply { setFullScreen() }
}

Or you can create an abstract class like this:

abstract class FullScreenBottomSheetDialogFragment : BottomSheetDialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return super.onCreateDialog(savedInstanceState).apply { setFullScreen() }
    }
}

A couple of notes:

  • you can use the setFullScreenExtension for regular BottomSheetDialog and not just for its BottomSheetDialogFragment version
  • the solution is not setting any custom dialog listeners under the hood (like setOnShowListener) so you can still use them in your dialogs if you need to, i.e. they won't be overridden by the setFullScreen extension

Upvotes: 2

this worked perfectly for me:

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

Upvotes: 0

hamid keyhani
hamid keyhani

Reputation: 461

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
    }

Upvotes: 4

Tushar Ahmed
Tushar Ahmed

Reputation: 41

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
}

Upvotes: 1

manu mathew
manu mathew

Reputation: 121

just add below line of code

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

and layout should be inside NestedScrollView

Upvotes: 6

Max M
Max M

Reputation: 81

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 >

Upvotes: 7

Shvedkov Ivan
Shvedkov Ivan

Reputation: 39

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.

Upvotes: 1

AliSh
AliSh

Reputation: 10649

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

Upvotes: 21

Kasun Thilina
Kasun Thilina

Reputation: 1681

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

Upvotes: 14

Yaqoob Bhatti
Yaqoob Bhatti

Reputation: 1575

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

Upvotes: 0

Stack Fox
Stack Fox

Reputation: 1211

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>

Upvotes: 11

lukaspp
lukaspp

Reputation: 1149

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

Upvotes: 65

Natig Babayev
Natig Babayev

Reputation: 3305

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

Upvotes: 25

cutiko
cutiko

Reputation: 10527

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

Upvotes: 35

Gabriele Mariotti
Gabriele Mariotti

Reputation: 364730

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

Upvotes: 93

Related Questions