防止 BottomSheetDialogFragment 覆盖导航栏

Posted

技术标签:

【中文标题】防止 BottomSheetDialogFragment 覆盖导航栏【英文标题】:Prevent BottomSheetDialogFragment covering navigation bar 【发布时间】: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的主题吗? 【参考方案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),因为此版本中引入了深色导航栏图标。在旧版本中,您会在白色背景上看到白色图标。

你需要:

    android:fitsSystemWindows="true" 添加到对话框布局的根目录。 正确修改您的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 为例

【讨论】:

我想在不自己编写此代码的情况下获得暗淡的效果。如果我找不到解决此问题的方法,这将是我最后的手段 这是不可能的,因为打开对话框会暂停当前活动,因此您将无法与活动进行任何交互

以上是关于防止 BottomSheetDialogFragment 覆盖导航栏的主要内容,如果未能解决你的问题,请参考以下文章

PHP 怎么防止GET方式提交重复数据?

如何防止网站被*** 防止网站数据被***篡改

MyBatis怎么防止SQL注入

如何彻底防止SQL注入?

怎么防止网站被***和防止服务器被黑

在winform当中提交数据,如何防止重复提交?