防止 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
添加对夜间模式的支持
【讨论】:
<item name="android:windowIsFloating">false</item>
解决了这个问题
谢谢兄弟,你拯救了我的一天。
你太棒了!几个月来一直在寻找这个答案......非常感谢!
谢谢,你真的拯救了我的一天!【参考方案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
中,唯一需要做的就是将底层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.parent
是 CoordinatorLayout
view.parent.parent.parent
是 CoordinatorLayout
的容器,其 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
导航栏颜色是必要的。并且还使用<item name="elevationOverlayEnabled">false</item>
来防止对话框背景看起来比导航栏更苍白。【参考方案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
部分添加<item name="android:windowIsFloating">false</item>
,然后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 覆盖导航栏的主要内容,如果未能解决你的问题,请参考以下文章