阻止BottomSheetDialogFragment覆盖导航栏

时间:2017-11-29 13:25:04

标签: android android-layout android-view navigationbar bottom-sheet

我使用非常天真的代码来显示底部对话框片段:

class LogoutBottomSheetFragment : BottomSheetDialogFragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.view_image_source_chooser, container, false)
        return view
    }
}

这就是我调用此对话框的方式:

LogoutBottomSheetFragment().show(supportFragmentManager, "logout")

但是我在下面的图片中看到了这个可怕的东西。 如何将导航栏保持为白色(背面/主页软件按钮所在的底栏)?

应用主题我正在使用:

 <!-- Base application theme. -->
<style name="BaseAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
</style

<style name="AppTheme" parent="BaseAppTheme">
    <item name="android:windowNoTitle">true</item>
    <item name="windowActionBar">false</item>

    <!-- Main theme colors -->
    <!--   your app branding color for the app bar -->
    <item name="android:colorPrimary">@color/colorPrimary</item>
    <!--   darker variant for the status bar and contextual app bars -->
    <item name="android:colorPrimaryDark">@android:color/white</item>
    <!--   theme UI controls like checkboxes and text fields -->
    <item name="android:colorAccent">@color/charcoal_grey</item>

    <item name="colorControlNormal">@color/charcoal_grey</item>
    <item name="colorControlActivated">@color/charcoal_grey</item>
    <item name="colorControlHighlight">@color/charcoal_grey</item>

    <item name="android:textColorPrimary">@color/charcoal_grey</item>
    <item name="android:textColor">@color/charcoal_grey</item>

    <item name="android:windowBackground">@color/white</item>
</style>

我还试图覆盖setupDialog而不是onCreateView,但仍然会发生:

    @SuppressLint("RestrictedApi")
override fun setupDialog(dialog: Dialog, style: Int) {
    super.setupDialog(dialog, style)
    val view = View.inflate(context, R.layout. view_image_source_chooser,null)
    dialog.setContentView(view)
}

12 个答案:

答案 0 :(得分:8)

我有相同的problem,我终于找到了一个解决方案,该解决方案不是hacky或需要大量的代码。

此方法用LayerDrawable替换了窗口背景,该图层包含两个元素:背景昏暗和导航栏背景。

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

“ setLayerInsetTop”方法需要API 23,但那很好,因为在Android O(API 26)中引入了深色的导航栏图标。

因此,解决方案的最后一部分是像这样从底部的onCreate方法调用此方法。

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

我希望它能对您有所帮助,如果您发现无法解决此问题的设备或案例,请告诉我。

before and after

答案 1 :(得分:4)

有一种避免Java / Kotlin代码更改的方法,如今该问题可以用XML完全解决:

<style name="MyTheme" parent="Theme.MaterialComponents">
    <item name="bottomSheetDialogTheme">@style/BottomSheet</item>
</style>

<style name="BottomSheet" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
    <item name="android:windowIsFloating">false</item>
    <item name="android:statusBarColor">@android:color/transparent</item>         
    <item name="android:navigationBarColor">?android:colorBackground</item>
    <item name="android:navigationBarDividerColor">?android:colorBackground</item>
</style>

我也遇到了一个问题,我的主题/样式没有应用于BottomSheetDialogFragment内的视图,这是我在基础BottomSheetDialogFragment中修复该问题的方法:

override fun onGetLayoutInflater(savedInstanceState: Bundle?): LayoutInflater {
    val inflater = super.onGetLayoutInflater(savedInstanceState)
    val wrappedContext = ContextThemeWrapper(requireContext(), R.style.My_Theme)
    return inflater.cloneInContext(wrappedContext)
}

答案 2 :(得分:1)

BottomSheetDialogFragment延伸DialogFragment。在BottomSheetDialog里面,它在onCreateDialog

中创建了一个Dialog
public class BottomSheetDialogFragment extends AppCompatDialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new BottomSheetDialog(getContext(), getTheme());
    }

}

昏暗层是对话框的一个属性,适用于整个窗口。然后它只会覆盖状态栏。如果您需要没有底部按钮的昏暗图层,则必须通过在布局内显示图层并相应更改状态栏颜色来手动完成。

为对话碎片应用主题,如下所示

class LogoutBottomSheetFragment : BottomSheetDialogFragment() {
    init {
        setStyle(DialogFragment.STYLE_NORMAL,R.style.dialog);
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.view_image_source_chooser, container, false)
        return view
    }


}

样式为

 <style name="dialog" parent="Base.Theme.AppCompat.Dialog">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:backgroundDimEnabled">false</item>
</style>

答案 3 :(得分:1)

j2esu的答案效果很好。但是,如果您坚持使用“完全白色”导航栏,则必须忽略其中的一部分。

请注意,此版本适用于Android O(API 26),因为此版本中引入了深色导航栏图标。在旧版本中,您会在白色背景上看到白色图标。

您需要:

  1. android:fitsSystemWindows="true"添加到对话框布局的根。
  2. 正确修改Window中的Dialog

将此代码放入您onStart的孩子的BottomSheetDialogFragment中。如果您使用的是设计库而不是材料库,请使用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);
    }
}

结果可能如下所示:

White navigation bar on Android P in dialog

答案 4 :(得分:1)

BottomSheetDialogFragment中,唯一需要做的就是将基础CoordinatorLayout fitSystemWindows的容器设置为false

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    (view!!.parent.parent.parent as View).fitsSystemWindows = false
}
  • view是您的布局
  • view.parent是一个包含您的视图的FrameLayout
  • view.parent.parentCoordinatorLayout
  • view.parent.parent.parentCoordinatorLayout的容器,默认情况下其fitsSystemWindow设置为true

这可确保将整个BottomSheetDialogFragment绘制在导航栏下方。然后,您可以将fitsSystemWindows设置为自己的容器。

您从其他答案中尤其不需要的是:

  • hacky findViewById带有系统ID的引用,该ID可能会发生变化,
  • getWindow()getDialog()的引用,
  • 在导航栏位置未设置可绘制对象。

此解决方案适用于使用BottomSheetDialogFragment创建的onCreateView,但我没有检查onCreateDialog

答案 5 :(得分:1)

我知道这里已经有很多解决方案,但是对我来说似乎所有解决方案都太多了,所以我发现了这个非常简单的解决方案here,功劳归于Arthur Nagy

只需覆盖BottomSheetDialogFragment中的getTheme方法:

override fun getTheme(): Int  = R.style.Theme_NoWiredStrapInNavigationBar

和styles.xml中的

<style name="Theme.NoWiredStrapInNavigationBar" parent="@style/Theme.Design.BottomSheetDialog">
    <item name="android:windowIsFloating">false</item>
    <item name="android:navigationBarColor">@color/bottom_sheet_bg</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

Kaboom,就这样。

您还可以通过更改@color/bottom_sheet_bg资源文件夹中的颜色values-night来增加对夜间模式的支持

答案 6 :(得分:1)

我只是在 style.xml 的 <item name="android:windowIsFloating">false</item> 部分添加了 BottomSheetDialog,然后当 BottomSheetDialog 打开时导航栏不会变暗。

答案 7 :(得分:0)

使用Follow API设置setContentView,而不是覆盖onCreateView。

        val dialog = BottomSheetDialog(context)
        dialog.setContentView(R.layout.your_layout)

BottomSheetDialog.setContentView将为BottomSheetDialog设置正确的行为。您可以看到源代码:

       public void setContentView(@LayoutRes int layoutResId) {
              super.setContentView(this.wrapInBottomSheet(layoutResId, (View)null, (LayoutParams)null));
       }

       private View wrapInBottomSheet(int layoutResId, View view, LayoutParams params) {
               FrameLayout container = (FrameLayout)View.inflate(this.getContext(), layout.design_bottom_sheet_dialog, (ViewGroup)null);
               CoordinatorLayout coordinator = (CoordinatorLayout)container.findViewById(id.coordinator);
               if (layoutResId != 0 && view == null) {
                  view = this.getLayoutInflater().inflate(layoutResId, coordinator, false);
               }
               // ... more stuff
       }

答案 8 :(得分:0)

不需要代码!使用材料组件:

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

答案 9 :(得分:0)

为了不覆盖背景、按钮样式和文本样式等其他样式,需要使用 ThemeOverlay

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    ...
    <item name="bottomSheetDialogTheme">@style/ThemeOverlay.AppTheme.BottomSheetDialog</item>
</style>

<style name="ThemeOverlay.AppTheme.BottomSheetDialog" parent="ThemeOverlay.MaterialComponents.BottomSheetDialog">
    <item name="android:windowIsFloating">false</item>
    <item name="android:windowLightNavigationBar">true</item>
    <item name="android:navigationBarColor">#FFFFFF</item>
</style>

答案 10 :(得分:-1)

不要使用BottomSheetDialogFragment。我更喜欢通过在协调器布局中包装布局并将BottomSheetBehaiviour附加到该布局来添加底部页面

您可以关注this作为示例

答案 11 :(得分:-1)

我遇到了同样的问题。在调查sources后,我找到了一个解决方法(有点hacky,但我找不到其他选择)。

public class YourDialog extends BottomSheetDialogFragment {

    //your code

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new FitSystemWindowsBottomSheetDialog(getContext());
    }
}

public class FitSystemWindowsBottomSheetDialog extends BottomSheetDialog {

    public FitSystemWindowsBottomSheetDialog(Context context) {
        super(context);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getWindow() != null && Build.VERSION.SDK_INT >= 21) {
            findViewById(android.support.design.R.id.coordinator).setFitsSystemWindows(false);
            findViewById(android.support.design.R.id.container).setFitsSystemWindows(false);
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS |
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
    }
}

最后,不要忘记在对话框布局的根目录中添加 android:fitsSystemWindows =&#34; true&#34;

希望它有所帮助。

相关问题