Я выяснил кучу вещей, которые могут помочь другим людям. Сначала я отвечу на исходный вопрос, затем объясню, почему он работает, с некоторыми альтернативами для людей, борющихся с той же проблемой, с которой я столкнулся: Как правильно отобразить полноэкранное приложение на телефоне с вырезами почтовый ящик моего приложения . ПРИМЕЧАНИЕ. Все мои макеты выполнены программно (у меня даже нет файлов xml в каталоге макетов). Это было сделано по кросс-платформенным причинам, и, возможно, именно поэтому у меня возникла эта проблема, а другие нет.
OP Ответ
Используйте следующий код для получения вырезов на дисплее (с approriate null проверяет):
<activity>.getWindowManager().getDefaultDisplay().getCutout();
В моем тестировании это вернуло правильные вырезы в onConfigurationChanged
и не равно нулю при вызове за пределами onAttachedToWindow
(если, конечно, есть вырезы) .
Объяснено
getWindow().getDecorView().getRootWindowInsets().getDisplayCutout()
всегда имеет значение NULL, если доступ осуществляется за пределами функции onAttachedToWindow
, когда ваше приложение полноэкранное; Я не нашел способ обойти это. Если вы выводите свое приложение из полноэкранного режима следующим образом:
rootView.setSystemUiVisibility(0
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
Вы получите значение из getDisplayCutout при вызове его извне onAttachedToWindow
. Это, конечно, не решение. Хуже того, значение, которое вы получаете от этого вызова функции, когда оно внутри onConfigurationChanged
, в большинстве случаев кажется неправильным ; он устарел (показывает расположение вырезов до изменения ориентации) и иногда полностью пропускает вырез (при наличии нескольких вырезов), что приводит к неправильным значениям safeInsets.
Полезные наблюдения
Я обнаружил некоторые полезные вещи, которые нужно знать при попытке решить эту проблему, которые могут помочь другим людям, которые пытаются разобраться с вырезами. В моем сценарии у меня есть полноэкранное приложение, и я не хочу добавлять сложность, пытаясь использовать весь экран, и мне нужно настроить макет в зависимости от размера и расположения выреза. Идеальная обработка вырезов - это гораздо больше усилий, чем того стоит для меня как отдельного разработчика, работающего над приложением.
Все, что я хочу, - это иметь возможность надежно выкладывать все мои виды программным способом без необходимости работать с вырезами. ; это означает, что все, что мне нужно знать, это размер и положение прямоугольника, в котором можно безопасно разместить мой контент. Это звучит просто, но было затруднено вышеприведенной проблемой, и некоторые странности в почтовом ящике Android содержимого, где используются системные оверлеи и вырезы.
Ниже приведены некоторые советы о том, как это можно сделать с различными результаты.
Получение доступного размера экрана (полноэкранное приложение)
Метод 1
Доступный размер экрана можно получить с помощью
<activity>.getWindowManager().getDefaultDisplay().getSize()
Этот размер отображается в всегда используйте безопасно, с или без вырезов, при использовании LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
для автоматической вставки содержимого, чтобы избежать вырезов.
Хотя, похоже, резервируется место для панели навигации. При установленных следующих флагах:
setSystemUiVisibility(0
| View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // hide nav bar
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // hide status bar
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
| View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY // if they swipe to show the soft-navigation, hide it again after a while.
);
Появляется функция getSize()
, чтобы зарезервировать место для панели навигации в нижней части экрана в портретной ориентации. Если вы используете LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
, то этот размер будет компенсировать вырезы, что означает, что ваш контент может уместиться в предоставленном пространстве, но также будет зарезервировано дополнительное пространство для панели навигации. Если вы используете устройство без вырезов, в нижней части по-прежнему будет зарезервировано место для панели навигации. Это не то, что мне нужно.
Метод 2
Другой метод получения размера экрана - сделать
DisplayMetrics metrics = new DisplayMetrics();
MainActivity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
. Это приведет к получению фактического размера экрана. в пикселях, от края до края, игнорируя системные наложения или полосы и вырезы в телефоне. Это выглядит как лучший способ для go для полноэкранного приложения в сочетании с точным и надежным методом определения вырезок. Строка состояния и мягкая навигация будут немного странным образом перекрываться с пустым пространством на краю вашего приложения, когда пользователь проведет пальцем, чтобы показать их, но это, безусловно, лучшее решение, которое я нашел, где я могу избежать попыток фактически обернуть вокруг вырезы с моим макетом.
Используйте LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
, затем используйте вырезы на экране, чтобы уменьшить и переместить ваше root представление.
// Get the actual screen size in pixels
DisplayMetrics metrics = new DisplayMetrics();
MainActivity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
int width = metrics.widthPixels;
int height = metrics.heightPixels;
// if there are no cutouts, top and left offsets are zero
int top = 0;
int left = 0;
// get any cutouts
DisplayCutout displayCutout = MainActivity.getWindowManager().getDefaultDisplay().getCutout();
// check if there are any cutouts
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// add safe insets together to shrink width and height
width -= (displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight());
height -= (displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom());
// NOTE:: with LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES, we can render on the whole screen
// NOTE:: and therefore we CAN and MUST set the top/left offset to avoid the cutouts.
top = displayCutout.getSafeInsetTop();
left = displayCutout.getSafeInsetLeft();
}
Это метод, который я в конечном итоге использовал, и пока не нашли никаких проблем с ним.
Метод 3
Здесь мы используем тот же метод, что и в # 2 , чтобы определить ширину и высоту, которые у нас есть. , но используя LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
, чтобы Android занимал позицию этого контента. Я думал, что это будет работать нормально, в конце концов, я не заинтересован в рендеринге в вырезанном пространстве Это вызывает странную проблему в портретном режиме - высота, которую android зарезервировано для строки состояния, была больше , чем высота верхнего выреза. Это приводит к тому, что мой код вычисляет, что у меня на больше высоты, чем на самом деле, и в результате мой контент обрезается внизу.
Если бы вы могли определить высоту в строке состояния можно настроить высоту, которую вы рассчитываете соответствующим образом, но меня это не беспокоило, так как это кажется худшим решением. Я, кстати, некоторое время пытался найти высоту строки состояния, но не смог с ней справиться (кажется, что ноль скрыт).
Заключение
Использование <activity>.getWindowManager().getDefaultDisplay().getCutout();
чтобы получить вырезы дисплея. После этого просто решите, какой метод использовать, чтобы определить, где находится безопасная область для визуализации.
Я рекомендую метод 2