【问题标题】:Prevent BottomSheetDialogFragment covering navigation bar防止 BottomSheetDialogFragment 覆盖导航栏
【发布时间】:2018-05-13 05:31:21
【问题描述】:

我正在使用非常幼稚的代码来显示底部工作表对话框片段:

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

【问题讨论】:

  • 您确定它已附加到您的CoordinatorLayout 吗?
  • 如何显示片段?
  • @JJ86 是的,活动根是协调器布局,但我怀疑它是否对这个问题有任何影响,因为这是一个对话框片段。
  • @KalaBalik 更新了我的问题
  • 可以分享styles.xml,具体是Activity的主题吗?

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


【解决方案1】:

我也有同样的problem,我终于找到了一个既不老套又不需要大量代码的解决方案。

此方法将窗口背景替换为包含两个元素的 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;
}

希望对您有所帮助,如果您发现无法使用此解决方案的设备或机箱,请告诉我。

【讨论】:

  • 嘿可能不是上下文,但你能告诉我如何为 BottomSheetDialog 圆角
  • 这很简单:你必须在你的应用程序主题中覆盖bottomSheetTheme,在你的bottomSheetTheme中覆盖bottomSheetStyle。在您的 bottomSheetStyle 中,您现在可以将背景设置为具有矩形形状和角的可绘制对象。就是这样。
  • 这是最干净的答案。另外,如果你只想使用activity的导航栏颜色,你可以使用navigationBarDrawable.setColor(activity.getWindow().getNavigationBarColor())。
  • 这是最好的答案,它还允许您无缝显示底部工作表并保持导航栏的主题不使其变暗:)
  • 在你的回答中你说“API 26”,但在代码中你使用API 27。这是为什么呢?
【解决方案2】:

我知道这里已经有很多解决方案,但对我来说似乎都太多了,所以我找到了这个非常简单的解决方案 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>

您也可以通过更改 values-night assets 文件夹中的颜色 @color/bottom_sheet_bg 添加对夜间模式的支持

【讨论】:

  • &lt;item name="android:windowIsFloating"&gt;false&lt;/item&gt; 解决了这个问题
  • 谢谢兄弟,你拯救了我的一天。
  • 你太棒了!几个月来一直在寻找这个答案......非常感谢!
  • 谢谢,你真的拯救了我的一天!
【解决方案3】:

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

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

你需要:

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

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

结果可能如下所示:

【讨论】:

    【解决方案4】:

    BottomSheetDialogFragment中,唯一需要做的就是将底层CoordinatorLayoutfitSystemWindows的容器设置为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,可能会发生变化,
    • 参考getWindow()getDialog()
    • 没有可在导航栏位置设置可绘制对象。

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

    【讨论】:

    • fitsSystemWindows 使对话框粘在屏幕底部,这使得导航栏被绘制在它上面,这与预期的不太一样
    • 您需要在对话框中添加边距以解决此问题(将 fitsystemwindows 添加到需要超出导航栏的容器中,如答案中所述)。如果您在应用顶部绘制导航栏,则您需要对下方绘制的内容负责。
    【解决方案5】:

    有一种方法可以避免更改 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)
    }
    

    【讨论】:

    • 谢谢!使用深色主题,对我来说 ?colorSurface 导航栏颜色是必要的。并且还使用&lt;item name="elevationOverlayEnabled"&gt;false&lt;/item&gt; 来防止对话框背景看起来比导航栏更苍白。
    【解决方案6】:

    无需代码!使用材质组件:

    <style name="Theme.YourApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        ...
        <item name="bottomSheetDialogTheme">@style/ThemeOverlay.YourApp.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.YourApp.BottomSheet</item>
        <item name="android:windowIsFloating">false</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:navigationBarColor">?colorSurface</item>
    </style>
    

    【讨论】:

    • 根据 material.io 上的文档,这似乎是最正确的答案。但是,我似乎无法让它工作......没有任何变化:导航栏颜色和底部工作表小部件的样式都没有。任何提示我可能做错了什么?谢谢!
    【解决方案7】:

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

    【讨论】:

      【解决方案8】:

      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>
      

      【讨论】:

      • ...then you have to do manually by showing a layer inside layout and changing status bar colour accordingly 怎么样?不是这个问题吗?您的答案看起来不完整。
      • @azizbekian 使用答案中指定的样式检查手机中的暗淡是否消失。我没有那种手机可以测试
      【解决方案9】:

      使用跟随 API 来设置内容视图,而不是覆盖 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
             }
      

      【讨论】:

        【解决方案10】:

        为了不覆盖背景、按钮样式和文本样式等其他样式,需要使用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>
        

        【讨论】:

          【解决方案11】:

          我遇到了同样的问题。在查看sources 后,我找到了一种解决方法(有点老套,但我没有找到替代方法)。

          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="true"

          希望对你有帮助。

          【讨论】:

            【解决方案12】:

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

            您可以关注this 为例

            【讨论】:

            • 我想在不自己编写此代码的情况下获得暗淡的效果。如果我找不到解决此问题的方法,这将是我最后的手段
            • 这是不可能的,因为打开对话框会暂停当前活动,因此您将无法与活动进行任何交互
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多