Prevent BottomSheetDialogFragment covering navigation bar

前端 未结 10 495
轮回少年
轮回少年 2020-12-12 23:49

I\'m using really naive code to show a bottom sheet dialog fragment:

class LogoutBottomSheetFragment : BottomSheetDialogFragment() {

    override fun onCrea         


        
相关标签:
10条回答
  • 2020-12-13 00:37

    No code required! Using Material Components:

    <style name="Theme.YourApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
        <item name="bottomSheetDialogTheme">@style/ThemeOverlay.Planner.BottomSheetDialog</item>
    </style>
    
    <style name="Widget.YourApp.BottomSheet" parent="Widget.MaterialComponents.BottomSheet"/>
    
    <style name="ThemeOverlay.YourApp.BottomSheetDialog" parent="@style/ThemeOverlay.MaterialComponents.BottomSheetDialog">
        <item name="bottomSheetStyle">@style/Widget.Planner.BottomSheet</item>
        <item name="android:windowIsFloating">false</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:navigationBarColor">?colorSurface</item>
    </style>
    
    0 讨论(0)
  • 2020-12-13 00:38

    I had the same problem and I finally found a solution which is not hacky or needs an orbitant amount of code.

    This Method replaced the window background with a LayerDrawable which consists of two elements: the background dim and the navigation bar background.

    @RequiresApi(api = Build.VERSION_CODES.M)
    private void setWhiteNavigationBar(@NonNull Dialog dialog) {
        Window window = dialog.getWindow();
        if (window != null) {
            DisplayMetrics metrics = new DisplayMetrics();
            window.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    
            GradientDrawable dimDrawable = new GradientDrawable();
            // ...customize your dim effect here
    
            GradientDrawable navigationBarDrawable = new GradientDrawable();
            navigationBarDrawable.setShape(GradientDrawable.RECTANGLE);
            navigationBarDrawable.setColor(Color.WHITE);
    
            Drawable[] layers = {dimDrawable, navigationBarDrawable};
    
            LayerDrawable windowBackground = new LayerDrawable(layers);
            windowBackground.setLayerInsetTop(1, metrics.heightPixels);
    
            window.setBackgroundDrawable(windowBackground);
        }
    }
    

    The method "setLayerInsetTop" requieres the API 23 but thats fine because dark navigation bar icons were introduced in Android O (API 26).

    So the last part of the solution is to call this method from your bottom sheets onCreate method like this.

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = super.onCreateDialog(savedInstanceState);
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
            setWhiteNavigationBar(dialog);
        }
    
        return dialog;
    }
    

    I hope it helps and please let me know if you find a device or case in which this solution does not work.

    0 讨论(0)
  • 2020-12-13 00:38

    Answer from j2esu works pretty well. However if you insist on 'completely white' navigation bar you have to omit part of it.

    Please note that this solution is applicable from Android O (API 26) since dark navigation bar icons were introduced in this version. On older versions you would get white icons on white background.

    You need to:

    1. Add android:fitsSystemWindows="true" to root of your dialog layout.
    2. Modify Window of your Dialog properly.

    Place this code to onStart of your child of BottomSheetDialogFragment. If you are using design library instead of material library use android.support.design.R.id.container.

    @Override
    public void onStart() {
        super.onStart();
        if (getDialog() != null && getDialog().getWindow() != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Window window = getDialog().getWindow();
            window.findViewById(com.google.android.material.R.id.container).setFitsSystemWindows(false);
            // dark navigation bar icons
            View decorView = window.getDecorView();
            decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
        }
    }
    

    Result might look like this:

    0 讨论(0)
  • 2020-12-13 00:44

    In BottomSheetDialogFragment, the only thing that needs to be done is to set the container of the underlying CoordinatorLayout fitSystemWindows to false.

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        (view!!.parent.parent.parent as View).fitsSystemWindows = false
    }
    
    • view is your layout
    • view.parent is a FrameLayout containing your view
    • view.parent.parent is the CoordinatorLayout
    • view.parent.parent.parent is the container for CoordinatorLayout which has its fitsSystemWindow set to true by default.

    This ensures that the whole BottomSheetDialogFragment is drawn underneath the navigation bar. Then you can set the fitsSystemWindows to your own containers accordingly.

    What you don't need from the other answers in particular is:

    • hacky findViewById with reference to system ids, which are subject to change,
    • reference to getWindow() or getDialog(),
    • no drawables to be set in the place of navigation bar.

    This solution works with BottomSheetDialogFragment created with onCreateView, I did not check onCreateDialog.

    0 讨论(0)
提交回复
热议问题