Я только что нашел простое и надежное решение, если вы используете системный интерфейс (https://developer.android.com/training/system-ui/immersive.html).
Это работает в случае, когда вы используете View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
, например. если вы используете CoordinatorLayout
.
Он не будет работать для WindowManager.LayoutParams.FLAG_FULLSCREEN
(тот, который вы также можете установить в теме с помощью android:windowFullscreen
), но вы можете добиться аналогичного эффекта с SYSTEM_UI_FLAG_LAYOUT_STABLE
(который "имеет тот же визуальный эффект" в соответствии с в документы ) и это решение должно снова работать.
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION /* If you want to hide navigation */
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
Я проверил это на своем устройстве под управлением Marshmallow.
Ключ в том, что программные клавиатуры также являются одним из системных окон (таких как строка состояния и панель навигации), поэтому отправляемая системой WindowInsets
содержит точную и надежную информацию о ней.
Для случая использования, такого как DrawerLayout
, где мы пытаемся рисовать за строкой состояния, мы можем создать макет, который игнорирует только верхнюю вставку и применяет нижнюю вставку, которая отвечает за программную клавиатуру.
Вот мой заказ FrameLayout
:
/**
* Implements an effect similar to {@code android:fitsSystemWindows="true"} on Lollipop or higher,
* except ignoring the top system window inset. {@code android:fitsSystemWindows="true"} does not
* and should not be set on this layout.
*/
public class FitsSystemWindowsExceptTopFrameLayout extends FrameLayout {
public FitsSystemWindowsExceptTopFrameLayout(Context context) {
super(context);
}
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setPadding(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
return insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
} else {
return super.onApplyWindowInsets(insets);
}
}
}
и использовать его:
<com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Your original layout here -->
</com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout>
Теоретически это должно работать для любого устройства без безумной модификации, гораздо лучше, чем любой взлом, который пытается использовать в качестве эталона случайный 1/3
или 1/4
размера экрана.
(для него требуется API 16+, но я использую полноэкранный режим только на Lollipop + для рисования за строкой состояния, поэтому в этом случае это лучшее решение.)