Round corner for BottomSheetDialogFragment

2020-01-27 11:39发布

问题:

i have an custom BttomSheetDialogFragment and i want to have round corners in top of Bottom View

this is my Custom class that inflating my layout that i want to appear from bottom

View mView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    mView = inflater.inflate(R.layout.charge_layout, container, false);
    initChargeLayoutViews();
    return mView;
}

and also i have this xml resource file as background :

<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<corners android:topRightRadius="35dp"
    android:topLeftRadius="35dp"
    />
<solid android:color="@color/white"/>

<padding android:top="10dp"
    android:bottom="10dp"
    android:right="16dp"
    android:left="16dp"/>

but the problem is, when i set this resource file as background of my Layout's root element , the corners still are not rounded

and i can't use below code :

    this.getDialog().getWindow().setBackgroundDrawableResource(R.drawable.charge_layout_background);

cuz its override default background of BottomSheetDialog and there wont be any semi-transparent gray color above my Bottom View

回答1:

Create a custom drawable rounded_dialog.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@android:color/white"/>
    <corners android:topLeftRadius="16dp"
        android:topRightRadius="16dp"/>

</shape>

Then override bottomSheetDialogTheme on styles.xml using the drawable as background:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">       
    <item name="bottomSheetDialogTheme">@style/AppBottomSheetDialogTheme</item>
</style>

<style name="AppBottomSheetDialogTheme"
    parent="Theme.Design.Light.BottomSheetDialog">
    <item name="bottomSheetStyle">@style/AppModalStyle</item>
</style>

<style name="AppModalStyle"
    parent="Widget.Design.BottomSheet.Modal">
    <item name="android:background">@drawable/rounded_dialog</item>
</style>

This will change all the BottomSheetDialogs of your app.



回答2:

The BottomSheetDialog is setting a default white background color , this is why the corners are not visible, In order to show them you need to make the background of the dialog transparent by overriding the style of the BottomSheetDialog.

Define this style In your res/values/styles/styles.xml

<style name="BottomSheetDialog" parent="Theme.Design.Light.BottomSheetDialog">
    <item name="bottomSheetStyle">@style/bottomSheetStyleWrapper</item>
</style>

<style name="bottomSheetStyleWrapper" parent="Widget.Design.BottomSheet.Modal">
    <item name="android:background">@android:color/transparent</item>
</style>

And set this style to your BottomSheetDialog

View view = getLayoutInflater().inflate(R.layout.chooser_bottom_sheet, null);
BottomSheetDialog dialog = new BottomSheetDialog(this,R.style.BottomSheetDialog); // Style here
dialog.setContentView(view);
dialog.show();


回答3:

With the new Material Component library you can customize the shape of your component using the shapeAppearanceOverlay attribute in your style.

Just use the BottomSheetDialogFragment overriding the onCreateView method and then define your custom style for Bottom Sheet Dialogs.

Define the bottomSheetDialogTheme attribute in styles.xml in your app theme:

  <!-- Base application theme. -->
  <style name="AppTheme" parent="Theme.MaterialComponents.Light">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    ....
    <item name="bottomSheetDialogTheme">@style/CustomBottomSheetDialog</item>
  </style>

Then just define your favorite shape with shapeAppearanceOverlay

  <style name="CustomBottomSheetDialog" parent="@style/ThemeOverlay.MaterialComponents.BottomSheetDialog">
    <item name="bottomSheetStyle">@style/CustomBottomSheet</item>
  </style>

  <style name="CustomBottomSheet" parent="Widget.MaterialComponents.BottomSheet">
    <item name="shapeAppearanceOverlay">@style/CustomShapeAppearanceBottomSheetDialog</item>
  </style>

  <style name="CustomShapeAppearanceBottomSheetDialog" parent="">
    <item name="cornerFamily">rounded</item>
    <item name="cornerSizeTopRight">16dp</item>
    <item name="cornerSizeTopLeft">16dp</item>
    <item name="cornerSizeBottomRight">0dp</item>
    <item name="cornerSizeBottomLeft">0dp</item>
  </style>

It requires the version 1.1.0 (currently the latest version is 1.1.0-beta02).



回答4:

create a shape named rounded_corners_shape

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <corners
        android:topLeftRadius="8dp"
        android:topRightRadius="8dp"/>
    <solid android:color="@color/white"/>

</shape>

define a style

  <style name="AppBottomSheetDialogTheme"
           parent="Theme.Design.Light.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/AppModalStyle</item>
    </style>

    <style name="AppModalStyle" parent="Widget.Design.BottomSheet.Modal">
        <item name="android:background">@drawable/rounded_corners_shape</item>
    </style>

use this style on your custom BottomSheetDialogFragment like this, it will be work!

 public class CustomDialogFragment extends BottomSheetDialogFragment {
      @Override
      public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NORMAL, R.style. AppBottomSheetDialogTheme);
      }

      ...
    }


回答5:

I was checking the same thing today and yes you were right about following code

this.getDialog().getWindow().setBackgroundDrawableResource(R.drawable.charge_layout_background);

this applies to fragment background, so instead you should get the bottomsheet view from dialog window and change the background here is the code

 @SuppressLint("RestrictedApi")
    @Override
    public void setupDialog(Dialog dialog, int style) {
        super.setupDialog(dialog, style);
        View rootView = getActivity().getLayoutInflater().inflate(R.layout.view_member_info,null,false);
        unbinder = ButterKnife.bind(this, rootView);
        adjustUIComponents();
        dialog.setContentView(rootView);
        FrameLayout bottomSheet = (FrameLayout) dialog.getWindow().findViewById(android.support.design.R.id.design_bottom_sheet);
        bottomSheet.setBackgroundResource(R.drawable.container_background);
    }

here bottomsheet is the actual view you want to change.



回答6:

Answer by Koma Yip from another question worked for me, you should try it.

Create a xml in drawable , say dialog_bg.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/white"/>
    <corners android:radius="30dp" />
    <padding
        android:left="10dp"
        android:top="10dp"
        android:right="10dp"
        android:bottom="10dp" />
</shape>

put this in your layout xml root node:

set it as the background in your layout xml

android:background="@drawable/dialog_bg"

and in onCreateView() put this:

set the background of your dialog to transparent

dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));


回答7:

Add these two methods in your BottomsheetDialogFragment class.

public void setDialogBorder(Dialog dialog) {
        FrameLayout bottomSheet = (FrameLayout) dialog.getWindow().findViewById(android.support.design.R.id.design_bottom_sheet);
        bottomSheet.setBackground(new ColorDrawable(Color.TRANSPARENT));
        setMargins(bottomSheet, 10, 0, 10, 20);
    }

    private void setMargins(View view, int left, int top, int right, int bottom) {
        if (view.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
            ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
            p.setMargins(left, top, right, bottom);
            view.requestLayout();
        }
    }

Now call setDialogBorder(dialog) method in setupDialog() method of your BottomsheetDialogFragment class.

Now create a shape file in your drawable folder.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="20dp" />

    <solid android:color="@color/white" />
    <stroke
        android:width="1dp"
        android:color="@color/transparent" />
</shape>

Now set background for the parent viewgroup dialog view in xml file.

android:background="@drawable/round_border_white"

Done!!



回答8:

This answer is only for the issue of setting the background color to Color.TRANSPARENT after setting up a drawable with rounded background to the layout.

None of the answer worked for me to set the background color to Color.TRANSPARENT except overriding setupDialog() solution:

@Override
public void setupDialog(Dialog dialog, int style) {
    super.setupDialog(dialog, style);
    View contentView = View.inflate(getContext(), 
R.layout.fragment_bottom_sheet, null);
    dialog.setContentView(contentView);
    ...
    ((View) contentView.getParent()).setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
}

BUT the contentView you set for dialog here is not the view you get in onViewCreated() when inflating in onCreateView(). It breaks the standard flow, so may issue troubles like you can't use View Bindings - Kotlin Android Extensions in onViewCreated()

So I tweak a bit to set the background in onActivityCreated():

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    (view?.parent as View).setBackgroundColor(Color.TRANSPARENT)
  }

Hope this help who got same trouble



回答9:

  1. Create a shape drawable .. which we will use as background for bottom sheet . Provide the appropriate value for radius of top left and right corner .

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <corners
            android:topLeftRadius="24dp"
            android:topRightRadius="24dp" />
        <padding android:top="2dp" />
        <solid android:color="@color/white" />
    </shape>
    
  2. Now create style for " Bottom sheet dialog fragment "

    <style name="BottomSheet" parent="@style/Widget.Design.BottomSheet.Modal">
            <item name="android:background">@drawable/drawable_bottomsheet_background</item>
        </style>
    
        <style name="BaseBottomSheetDialog" parent="@style/Theme.Design.Light.BottomSheetDialog">
            <item name="android:windowIsFloating">false</item>
            <item name="bottomSheetStyle">@style/BottomSheet</item>
        </style>
    
        <style name="BottomSheetDialogTheme" parent="BaseBottomSheetDialog" />
    
  3. Now create a custom class that will extend BottomSheetDilogFragment ,where you provide your style .

    open class CustomRoundBottomSheet : BottomSheetDialogFragment() {
    
        override fun getTheme(): Int = R.style.BottomSheetDialogTheme
    
        override fun onCreateDialog(savedInstanceState: Bundle?): Dialog = BottomSheetDialog(requireContext(), theme)
    
    }
    
  4. Now use this class wherever you want to have round corner bottom sheet . eg

    class BottomSheetSuccess : CustomRoundBottomSheet() {
    
        override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
            return inflater.inflate(R.layout.bottomsheet_shopcreate_success, container, false)
        }
    
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
        }
    
    } 
    


回答10:

If you use the last version of material component you just have to override ShapeAppearance.MaterialComponents.LargeComponent (as the bottom sheet use this shape) and set the value you want like :

 <style name="ShapeAppearance.YourApp.LargeComponent" parent="ShapeAppearance.MaterialComponents.LargeComponent">
        <item name="cornerFamily">rounded</item>
        <item name="cornerSize">12dp</item>
 </style>

And then set in your app style :

<item name="shapeAppearanceLargeComponent">@style/ShapeAppearance.YourApp.LargeComponent</item>

The solution of Gabriele Mariotti is similar and works too but this one is simpler.



回答11:

Another way to fix this issue is to extend BottomSheetDialog and create a custom class which suits your needs. You can do the same for layout xml file, and add background or any other customizations needed. This also has a benefit that you'll not be dependent on the id names used by Android(android.support.design.R.id.design_bottom_sheet), while changing the background (though the change of id name rarely happens AFAIK).



回答12:

Create a custom drawable with rounded corner and set it as background of your BottomSheetDialogFragment's layout root

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">

<solid android:color="@color/colorPrimary" />

<corners
    android:bottomLeftRadius="0dp"
    android:bottomRightRadius="0dp"
    android:topLeftRadius="12dp"
    android:topRightRadius="12dp" />

</shape>

And then simply add the below code to your BottomSheetDialogFragment class

@Override
public void setupDialog(Dialog dialog, int style) {
    super.setupDialog(dialog, style);
    View contentView = View.inflate(getContext(), 
R.layout.fragment_bottom_sheet, null);
    dialog.setContentView(contentView);

    CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) contentView.getParent())
            .getLayoutParams();
    CoordinatorLayout.Behavior behavior = params.getBehavior();
    ((View) contentView.getParent()).setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
}

You can even play with the params to set margin like below

params.setMargins(50, 0, 50, 0);


回答13:

You have to change the bottom sheet theme to achieve the top round layout

Create a custom drawable background_bottom_sheet_dialog_fragment.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="rectangle">
    <corners
       android:topLeftRadius="8dp"
        android:topRightRadius="8dp" />
    <padding android:top="0dp" />
    <solid android:color="@color/white" />
</shape>

Then override bottomSheetDialogTheme on styles.xml using the drawable as background:

<!--Bottom sheet-->
<style name="BottomSheet" parent="@style/Widget.Design.BottomSheet.Modal">
    <item 
    name="android:background">@drawable/background_bottom_sheet_dialog_fragment
    </item>
</style>

<style name="BaseBottomSheetDialog" 
    parent="@style/Theme.Design.Light.BottomSheetDialog">
    <item name="android:windowIsFloating">false</item>
    <item name="bottomSheetStyle">@style/BottomSheet</item>
</style>

<style name="BottomSheetDialogTheme" parent="BaseBottomSheetDialog" />

This will change the background layout of your bottom sheet

BottomSheetDialog

class SheetFragment() : BottomSheetDialogFragment() {

    lateinit var binding: SheetFragmentBinding;

  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog;
    val view = View.inflate(context, R.layout.fragment_bottom_sheet, null);

    binding = DataBindingUtil.bind(view)!!;
    binding.viewModel = SheetFragmentVM();

    dialog.setContentView(view);

    var bottomSheetBehavior = BottomSheetBehavior.from(view.parent as View);
    bottomSheetBehavior.setPeekHeight(BottomSheetBehavior.PEEK_HEIGHT_AUTO);

    bottomSheetBehavior.setBottomSheetCallback(object : 
     BottomSheetBehavior.BottomSheetCallback() {
        override fun onStateChanged(bottomSheet: View, newState: Int) {
            if (BottomSheetBehavior.STATE_EXPANDED == newState) {
               // do on STATE_EXPANDED
            }
            if (BottomSheetBehavior.STATE_COLLAPSED == newState) {
                // do on STATE_COLLAPSED
            }

            if (BottomSheetBehavior.STATE_HIDDEN == newState) {
                dismiss()

            }
        }

        override fun onSlide(bottomSheet: View, slideOffset: Float) {
           // do on slide
        }
    })

    return dialog
}