问题
I'm using really naive code to show a bottom sheet dialog fragment:
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
}
}
This is how I called this dialog:
LogoutBottomSheetFragment().show(supportFragmentManager, "logout")
But I get this horrible shown in the image below. How can I keep the navigation bar white (the bottom bar where the back/home software buttons are)?
App Theme I'm using:
<!-- 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>
I've also tried to override the setupDialog instead of the onCreateView, but still happens:
@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)
}
回答1:
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.
回答2:
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:
- Add
android:fitsSystemWindows="true"
to root of your dialog layout. - Modify
Window
of yourDialog
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:
回答3:
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 layoutview.parent
is a FrameLayout containing your viewview.parent.parent
is theCoordinatorLayout
view.parent.parent.parent
is the container forCoordinatorLayout
which has itsfitsSystemWindow
set totrue
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()
orgetDialog()
, - no drawables to be set in the place of navigation bar.
This solution works with BottomSheetDialogFragment
created with onCreateView
, I did not check onCreateDialog
.
回答4:
BottomSheetDialogFragment
extends DialogFragment
. Inside BottomSheetDialog it's creating a Dialog inside onCreateDialog
public class BottomSheetDialogFragment extends AppCompatDialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new BottomSheetDialog(getContext(), getTheme());
}
}
The dim layer is a property of dialog which is applying to whole window. Then only it will cover the status bar. If you need dim layer without bottom buttons, then you have to do manually by showing a layer inside layout and changing status bar colour accordingly.
Apply theme for dialogfragment as given below
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
}
}
With styles as
<style name="dialog" parent="Base.Theme.AppCompat.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
回答5:
I know there is many solutions here already but all of them seems too much to me, so I found this very simple solution here, credit goes to Arthur Nagy:
just override the getTheme method in the BottomSheetDialogFragment:
override fun getTheme(): Int = R.style.Theme_NoWiredStrapInNavigationBar
and in 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, thats it.
you can also add support for night mode by changing the color @color/bottom_sheet_bg
in the values-night
assets folder
回答6:
Use follow API to setContentView instead of overriding onCreateView.
val dialog = BottomSheetDialog(context)
dialog.setContentView(R.layout.your_layout)
BottomSheetDialog.setContentView will setup the correct behavior for BottomSheetDialog. You can see the source code:
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
}
回答7:
Don't use BottomSheetDialogFragment
.I would prefer use adding bottom sheet by wrapping the layout in the coordinator layout and attaching BottomSheetBehaiviour to that layout
You can follow this as an example
回答8:
I had the same problem. After looking into sources I found a workaround (a little bit hacky, but I found no alternatives).
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);
}
}
}
And, finally, don't forget to add android:fitsSystemWindows="true" at the root of your dialog layout.
Hope it helps.
来源:https://stackoverflow.com/questions/47553936/prevent-bottomsheetdialogfragment-covering-navigation-bar