Предотвращение отображения нижнего слояDialogFragment на панели навигации

Я использую действительно наивный код, чтобы показать фрагмент диалога нижнего листа:

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

Но я получаю это ужасно, как показано на изображении ниже. Как я могу сохранить навигационную панель белой (нижняя панель, где расположены кнопки программного обеспечения для задней панели/дома)?

81eMC.png

Тема приложения, которую я использую:

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

Ответ 1

У меня была та же проблема, и я наконец нашел решение, которое не является хакерским или требует большого количества кода.

Этот метод заменил фон окна на 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

Ответ 2

Ответ от 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

Ответ 3

В 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 со ссылкой на системные идентификаторы, которые могут быть изменены,
  • ссылка на getWindow() или getDialog(),
  • на панели навигации не должно быть никаких элементов рисования.

Это решение работает с BottomSheetDialogFragment, созданным с помощью onCreateView, я не проверял onCreateDialog.

Ответ 4

Я знаю, что здесь уже есть много решений, но все они кажутся мне слишком большими, поэтому я нашел это очень простое решение здесь, заслуга Артура Надя:

просто переопределите метод getTheme в BottomSheetDialogFragment:

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>

Кабум, вот и все.

Вы также можете добавить поддержку ночного режима, изменив цвет @color/bottom_sheet_bg в папке ресурсов values-night

Ответ 5

BottomSheetDialogFragment расширяет DialogFragment. Внутри BottomSheetDialog он создает диалог внутри onCreateDialog

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>

Ответ 6

Используйте 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
       }

Ответ 7

Не используйте BottomSheetDialogFragment Я бы предпочел использовать добавочный нижний лист, обернув макет в макете координатора и присоединив BottomSheetBehaiviour к этому макету

Вы можете следовать этому примеру

Ответ 8

У меня такая же проблема. Изучив источники, я нашел обходное решение (немного взломанное, но я не нашел альтернатив).

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" в корень вашего диалогового макета.

Надеюсь, поможет.