Чорну смугу навігації в нижній частині екрана в Android не можна легко зняти. Він є частиною Android з 3.0, як заміна апаратних кнопок. Ось малюнок:
Як я можу отримати розмір ширини та висоти цього елемента інтерфейсу у пікселях?
Чорну смугу навігації в нижній частині екрана в Android не можна легко зняти. Він є частиною Android з 3.0, як заміна апаратних кнопок. Ось малюнок:
Як я можу отримати розмір ширини та висоти цього елемента інтерфейсу у пікселях?
Відповіді:
Спробуйте нижче код:
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
}
return 0;
navigation_bar_height_landscape
висоту панелі навігації в ландшафтному режимі та navigation_bar_width
ширину вертикальної панелі навігації). Ви повинні дізнатися окремо, чи дійсно відображається панель навігації, наприклад, перевіривши наявність фізичної кнопки меню. Можливо, ви можете знайти інший спосіб у коді Android
Я отримую розмір панелі навігації, порівнюючи розмір екрана, застосований для додатків, з реальним розміром екрана. Я припускаю, що панель навігації присутня, коли розмір екрана, який можна застосувати, менший за розмір реального екрана. Потім я обчислюю розмір навігаційної панелі. Цей метод працює з API 14 і вище.
public static Point getNavigationBarSize(Context context) {
Point appUsableSize = getAppUsableScreenSize(context);
Point realScreenSize = getRealScreenSize(context);
// navigation bar on the side
if (appUsableSize.x < realScreenSize.x) {
return new Point(realScreenSize.x - appUsableSize.x, appUsableSize.y);
}
// navigation bar at the bottom
if (appUsableSize.y < realScreenSize.y) {
return new Point(appUsableSize.x, realScreenSize.y - appUsableSize.y);
}
// navigation bar is not present
return new Point();
}
public static Point getAppUsableScreenSize(Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size;
}
public static Point getRealScreenSize(Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
Point size = new Point();
if (Build.VERSION.SDK_INT >= 17) {
display.getRealSize(size);
} else if (Build.VERSION.SDK_INT >= 14) {
try {
size.x = (Integer) Display.class.getMethod("getRawWidth").invoke(display);
size.y = (Integer) Display.class.getMethod("getRawHeight").invoke(display);
} catch (IllegalAccessException e) {} catch (InvocationTargetException e) {} catch (NoSuchMethodException e) {}
}
return size;
}
ОНОВЛЕННЯ
Для рішення, яке враховує вирізи на екрані, перевірте відповідь Джона .
Висота NavigationBar змінюється для деяких пристроїв, але також для деяких орієнтацій. Спочатку ви повинні перевірити, чи є у пристрою навігаційна панель, потім, якщо пристрій - планшет чи не планшет (телефон), і, нарешті, потрібно подивитися орієнтацію пристрою, щоб отримати правильну висоту.
public int getNavBarHeight(Context c) {
int result = 0;
boolean hasMenuKey = ViewConfiguration.get(c).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
if(!hasMenuKey && !hasBackKey) {
//The device has a navigation bar
Resources resources = c.getResources();
int orientation = resources.getConfiguration().orientation;
int resourceId;
if (isTablet(c)){
resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android");
} else {
resourceId = resources.getIdentifier(orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_width", "dimen", "android");
}
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
}
}
return result;
}
private boolean isTablet(Context c) {
return (c.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
hasBackKey = false!
хоча це має бути правдою. Натомість працювали наступні: boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
Насправді панель навігації на планшетах (принаймні Nexus 7) має різний розмір у портреті та пейзажі, тому ця функція повинна виглядати приблизно так:
private int getNavigationBarHeight(Context context, int orientation) {
Resources resources = context.getResources();
int id = resources.getIdentifier(
orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape",
"dimen", "android");
if (id > 0) {
return resources.getDimensionPixelSize(id);
}
return 0;
}
context.getResources().getConfiguration().orientation
Я думаю, що краща відповідь є тут, оскільки це дозволяє вам отримати рівну висоту вирізу.
Виберіть свій кореневий вигляд і додайте setOnApplyWindowInsetsListener (або ви можете замінити на нього onApplyWindowInsets) та візьміть з нього insets.getSystemWindowInsets.
У своїй діяльності з камери я додаю в нижній макет прокладки, що дорівнює systemWindowInsetBottom. І, нарешті, це виправити проблему з вирізом.
з appcompat це так
ViewCompat.setOnApplyWindowInsetsListener(mCameraSourcePreview, (v, insets) -> {
takePictureLayout.setPadding(0,0,0,insets.getSystemWindowInsetBottom());
return insets.consumeSystemWindowInsets();
});
без appcompat, це:
mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })
setOnApplyWindowInsetsListener
замість цього? Якщо так, то як? і чому ви розмежували LOLLIPOP та решту?
Я сподіваюся, що це вам допоможе
public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public int getNavigationBarHeight()
{
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0 && !hasMenuKey)
{
return getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
Це мій код, щоб додати paddingRight та paddingBottom до виду, щоб уникнути панелі навігації. Я поєднав деякі відповіді тут і склав спеціальний пункт щодо орієнтації на пейзаж разом з isInMultiWindowMode. Ключ - прочитати навігацію_бар_висоту , а також перевірити config_showNavigationBar, щоб переконатися, що ми насправді повинні використовувати висоту.
Жодне з попередніх рішень не працювало для мене. Що стосується Android 7.0, ви повинні врахувати режим "Multi Window". Це порушує реалізацію, порівнюючи display.realSize з display.size, оскільки realSize дає розміри всього екрана (обидва розділених вікна), а розмір дає лише розміри вікна вашого додатка. Якщо встановити накладку на цю різницю, ваш весь погляд буде залишеним.
/** Adds padding to a view to dodge the navigation bar.
Unfortunately something like this needs to be done since there
are no attr or dimens value available to get the navigation bar
height (as of December 2016). */
public static void addNavigationBarPadding(Activity context, View v) {
Resources resources = context.getResources();
if (hasNavigationBar(resources)) {
int orientation = resources.getConfiguration().orientation;
int size = getNavigationBarSize(resources);
switch (orientation) {
case Configuration.ORIENTATION_LANDSCAPE:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
context.isInMultiWindowMode()) { break; }
v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
v.getPaddingRight() + size, v.getPaddingBottom());
break;
case Configuration.ORIENTATION_PORTRAIT:
v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
v.getPaddingRight(), v.getPaddingBottom() + size);
break;
}
}
}
private static int getNavigationBarSize(Resources resources) {
int resourceId = resources.getIdentifier("navigation_bar_height",
"dimen", "android");
return resourceId > 0 ? resources.getDimensionPixelSize(resourceId) : 0;
}
private static boolean hasNavigationBar(Resources resources) {
int hasNavBarId = resources.getIdentifier("config_showNavigationBar",
"bool", "android");
return hasNavBarId > 0 && resources.getBoolean(hasNavBarId);
}
Висота нижньої панелі навігації - 48 дп (як в портретному, так і в пейзажному режимі) і становить 42 дп при розміщенні планки вертикально.
Рішення, запропоноване Egidijus і прекрасно працює для Build.VERSION.SDK_INT> = 17
Але я отримав "NoSuchMethodException" під час виконання наступного оператора Build.VERSION.SDK_INT <17 на своєму пристрої:
Display.class.getMethod("getRawHeight").invoke(display);
Я змінив метод getRealScreenSize () для таких випадків:
else if(Build.VERSION.SDK_INT >= 14)
{
View decorView = getActivity().getWindow().getDecorView();
size.x = decorView.getWidth();
size.y = decorView.getHeight();
}
Я вирішив цю проблему для всіх пристроїв (включаючи Nexus 5, Samsung Galaxy Nexus 6 edge +, Samsung S10, Samsung Note II тощо). Я думаю, що це допоможе вам вирішити проблеми, залежні від пристрою.
Тут я додаю два типи кодів,
Код Java (для Native Android):
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.ViewConfiguration;
import android.view.WindowManager;
public class DeviceSpec {
private int resourceID = -1;
private Display display = null;
private DisplayMetrics displayMetrics = null;
private DisplayMetrics realDisplayMetrics = null;
private Resources resources = null;
private WindowManager windowManager = null;
public double GetNavigationBarHeight(Context context) {
try {
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
display = windowManager.getDefaultDisplay();
displayMetrics = new DisplayMetrics();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
realDisplayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);
display.getRealMetrics(realDisplayMetrics);
if(displayMetrics.heightPixels != realDisplayMetrics.heightPixels) {
resources = context.getResources();
return GetNavigationBarSize(context);
}
}
else {
resources = context.getResources();
resourceID = resources.getIdentifier("config_showNavigationBar", "bool", "android");
if (resourceID > 0 && resources.getBoolean(resourceID))
return GetNavigationBarSize(context);
}
}
catch (Exception e){
e.printStackTrace();
}
return 0;
}
private double GetNavigationBarSize(Context context) {
resourceID = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceID > 0 && ViewConfiguration.get(context).hasPermanentMenuKey())
return (resources.getDimensionPixelSize(resourceID) / displayMetrics.density);
return 0;
}
}
І код C # (для Xamarin Forms / Android)
int resourceId = -1;
IWindowManager windowManager = null;
Display defaultDisplay = null;
DisplayMetrics displayMatrics = null;
DisplayMetrics realMatrics = null;
Resources resources = null;
public double NavigationBarHeight
{
get
{
try
{
windowManager = Forms.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
defaultDisplay = windowManager.DefaultDisplay;
displayMatrics = new DisplayMetrics();
if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr2)
{
realMatrics = new DisplayMetrics();
defaultDisplay.GetMetrics(displayMatrics);
defaultDisplay.GetRealMetrics(realMatrics);
if (displayMatrics.HeightPixels != realMatrics.HeightPixels)
{
resources = Forms.Context.Resources;
return GetHeightOfNivigationBar();
}
}
else {
resources = Forms.Context.Resources;
resourceId = resources.GetIdentifier("config_showNavigationBar", "bool", "android");
if (resourceId > 0 && resources.GetBoolean(resourceId))
return GetHeightOfNivigationBar();
}
}
catch (Exception e) { }
return 0;
}
}
private double GetHeightOfNivigationBar()
{
resourceId = resources.GetIdentifier("navigation_bar_height", "dimen", "android");
if (!ViewConfiguration.Get(Forms.Context).HasPermanentMenuKey && resourceId > 0)
{
return resources.GetDimensionPixelSize(resourceId) / displayMatrics.Density;
}
return 0;
}
Display.getRealMetrics()
вимагає рівня API 17
screenOrientation
за замовчуванням, hasPermanentMenuKey
це false
лише якщо не обертається. [2] Коли screenOrientation
пейзаж, він потрапляє в displayMetrics.heightPixels != realDisplayMetrics.heightPixels)
інше місце. [3] Коли screenOrientation
портрет, hasPermanentMenuKey
є false
.
Поєднання відповіді від @egis та інших - це добре працює на різних пристроях, протестованих на Pixel EMU, Samsung S6, Sony Z3, Nexus 4. Цей код використовує розміри дисплея для перевірки наявності навігаційної панелі, а потім використовує фактичну розмір панелі навігації системи, якщо такий є
/**
* Calculates the system navigation bar size.
*/
public final class NavigationBarSize {
private final int systemNavBarHeight;
@NonNull
private final Point navBarSize;
public NavigationBarSize(@NonNull Context context) {
Resources resources = context.getResources();
int displayOrientation = resources.getConfiguration().orientation;
final String name;
switch (displayOrientation) {
case Configuration.ORIENTATION_PORTRAIT:
name = "navigation_bar_height";
break;
default:
name = "navigation_bar_height_landscape";
}
int id = resources.getIdentifier(name, "dimen", "android");
systemNavBarHeight = id > 0 ? resources.getDimensionPixelSize(id) : 0;
navBarSize = getNavigationBarSize(context);
}
public void adjustBottomPadding(@NonNull View view, @DimenRes int defaultHeight) {
int height = 0;
if (navBarSize.y > 0) {
// the device has a nav bar, get the correct size from the system
height = systemNavBarHeight;
}
if (height == 0) {
// fallback to default
height = view.getContext().getResources().getDimensionPixelSize(defaultHeight);
}
view.setPadding(0, 0, 0, height);
}
@NonNull
private static Point getNavigationBarSize(@NonNull Context context) {
Point appUsableSize = new Point();
Point realScreenSize = new Point();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (windowManager != null) {
Display display = windowManager.getDefaultDisplay();
display.getSize(appUsableSize);
display.getRealSize(realScreenSize);
}
return new Point(realScreenSize.x - appUsableSize.x, realScreenSize.y - appUsableSize.y);
}
}
Як отримати висоту панелі навігації та рядка стану. Цей код працює для мене на деяких пристроях Huawei та Samsung . Вище рішення Егіса добре, проте на деяких пристроях воно все ще неправильне. Отже, я його покращив.
Це код для отримання висоти рядка стану
private fun getStatusBarHeight(resources: Resources): Int {
var result = 0
val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
if (resourceId > 0) {
result = resources.getDimensionPixelSize(resourceId)
}
return result
}
Цей метод завжди повертає висоту панелі навігації, навіть якщо навігаційна панель прихована.
private fun getNavigationBarHeight(resources: Resources): Int {
val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
return if (resourceId > 0) {
resources.getDimensionPixelSize(resourceId)
} else 0
}
ПРИМІТКА. У Samsung A70 цей метод повертає висоту рядка стану + висоту панелі навігації. На інших пристроях (Huawei) він повертає лише висоту панелі навігації і повертає 0, коли навігаційна панель прихована.
private fun getNavigationBarHeight(): Int {
val display = activity?.windowManager?.defaultDisplay
return if (display == null) {
0
} else {
val realMetrics = DisplayMetrics()
display.getRealMetrics(realMetrics)
val metrics = DisplayMetrics()
display.getMetrics(metrics)
realMetrics.heightPixels - metrics.heightPixels
}
}
Це код для отримання висоти панелі навігації та рядка стану
val metrics = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getRealMetrics(metrics)
//resources is got from activity
//NOTE: on SamSung A70, this height = height of status bar + height of Navigation bar
//On other devices (Huawei), this height = height of Navigation bar
val navigationBarHeightOrNavigationBarPlusStatusBarHeight = getNavigationBarHeight()
val statusBarHeight = getStatusBarHeight(resources)
//The method will always return the height of navigation bar even when the navigation bar was hidden.
val realNavigationBarHeight = getNavigationBarHeight(resources)
val realHeightOfStatusBarAndNavigationBar =
if (navigationBarHeightOrNavigationBarPlusStatusBarHeight == 0 || navigationBarHeightOrNavigationBarPlusStatusBarHeight < statusBarHeight) {
//Huawei: navigation bar is hidden
statusBarHeight
} else if (navigationBarHeightOrNavigationBarPlusStatusBarHeight == realNavigationBarHeight) {
//Huawei: navigation bar is visible
statusBarHeight + realNavigationBarHeight
} else if (navigationBarHeightOrNavigationBarPlusStatusBarHeight < realNavigationBarHeight) {
//SamSung A70: navigation bar is still visible but it only displays as a under line
//navigationBarHeightOrNavigationBarPlusStatusBarHeight = navigationBarHeight'(under line) + statusBarHeight
navigationBarHeightOrNavigationBarPlusStatusBarHeight
} else {
//SamSung A70: navigation bar is visible
//navigationBarHeightOrNavigationBarPlusStatusBarHeight == statusBarHeight + realNavigationBarHeight
navigationBarHeightOrNavigationBarPlusStatusBarHeight
}
Я це зробив, він працює на кожному тестованому пристрої і навіть на емуляторах:
// Return the NavigationBar height in pixels if it is present, otherwise return 0
public static int getNavigationBarHeight(Activity activity) {
Rect rectangle = new Rect();
DisplayMetrics displayMetrics = new DisplayMetrics();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rectangle);
activity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
return displayMetrics.heightPixels - (rectangle.top + rectangle.height());
}
Ось як я це вирішив. Я зробив приховану нижню панель, яка потребувала набивання залежно від того, була панель навігації чи ні (ємнісний, на екрані чи просто попередній льодяник).
Вид
setPadding(0, 0, 0, Utils.hasNavBar(getContext()) ? 30 : 0);
Utils.java
public static boolean hasNavBar(Context context) {
// Kitkat and less shows container above nav bar
if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
return false;
}
// Emulator
if (Build.FINGERPRINT.startsWith("generic")) {
return true;
}
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
boolean hasNoCapacitiveKeys = !hasMenuKey && !hasBackKey;
Resources resources = context.getResources();
int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
boolean hasOnScreenNavBar = id > 0 && resources.getBoolean(id);
return hasOnScreenNavBar || hasNoCapacitiveKeys || getNavigationBarHeight(context, true) > 0;
}
public static int getNavigationBarHeight(Context context, boolean skipRequirement) {
int resourceId = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0 && (skipRequirement || hasNavBar(context))) {
return context.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
У моєму випадку, коли я хотів щось подібне:
Мені довелося дотримуватися того самого, що було запропоновано @Mdlc, але, ймовірно, трохи простіше (націлення лише > = 21):
//kotlin
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
val realSize = Point()
windowManager.defaultDisplay.getRealSize(realSize);
val usableRect = Rect()
windowManager.defaultDisplay.getRectSize(usableRect)
Toast.makeText(this, "Usable Screen: " + usableRect + " real:"+realSize, Toast.LENGTH_LONG).show()
window.decorView.setPadding(usableRect.left, usableRect.top, realSize.x - usableRect.right, realSize.y - usableRect.bottom)
Це працює і на ландшафті:
Редагувати Наведене вище рішення не працює належним чином у багатовіконному режимі, де корисний прямокутник не менший за рахунок навігаційної панелі, а також із-за нестандартного розміру вікна. Одне, що я помітив, - це те, що на панелі навігації в декількох вікнах не нависає додаток, тому навіть без змін накладки DecorView ми маємо правильну поведінку:
Зверніть увагу на різницю між навігаційною панеллю, що нависає над нижньою частиною програми, у цих сценаріях. На щастя, це легко виправити. Ми можемо перевірити, чи додаток має багато вікон. Код нижче також включає частину для обчислення та коригування положення панелі інструментів (повне рішення: https://stackoverflow.com/a/14213035/477790 )
// kotlin
// Let the window flow into where window decorations are
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN)
window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
// calculate where the bottom of the page should end up, considering the navigation bar (back buttons, ...)
val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
val realSize = Point()
windowManager.defaultDisplay.getRealSize(realSize);
val usableRect = Rect()
windowManager.defaultDisplay.getRectSize(usableRect)
Toast.makeText(this, "Usable Screen: " + usableRect + " real:" + realSize, Toast.LENGTH_LONG).show()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !isInMultiWindowMode) {
window.decorView.setPadding(usableRect.left, usableRect.top, realSize.x - usableRect.right, realSize.y - usableRect.bottom)
// move toolbar/appbar further down to where it should be and not to overlap with status bar
val layoutParams = ConstraintLayout.LayoutParams(appBarLayout.layoutParams as ConstraintLayout.LayoutParams)
layoutParams.topMargin = getSystemSize(Constants.statusBarHeightKey)
appBarLayout.layoutParams = layoutParams
}
Результат у спливаючому режимі Samsung:
Тестований код для отримання висоти панелі навігації (у пікселях):
public static int getNavBarHeight(Context c) {
int resourceId = c.getResources()
.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return c.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
Тестований код для отримання висоти рядка стану (у пікселях):
public static int getStatusBarHeight(Context c) {
int resourceId = c.getResources()
.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
return c.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
Перетворення пікселів у dp:
public static int pxToDp(int px) {
return (int) (px / Resources.getSystem().getDisplayMetrics().density);
}
У випадку Samsung S8 жоден із перерахованих вище методів не давав належної висоти смуги навігації, тому я використовував провайдер висоти клавіатури KeyboardHeightProvider . І це дало мені висоту в негативних значеннях, і для мого розташування макета я скоригував це значення в розрахунках.
Ось KeyboardHeightProvider.java
:
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager.LayoutParams;
import android.widget.PopupWindow;
/**
* The keyboard height provider, this class uses a PopupWindow
* to calculate the window height when the floating keyboard is opened and closed.
*/
public class KeyboardHeightProvider extends PopupWindow {
/** The tag for logging purposes */
private final static String TAG = "sample_KeyboardHeightProvider";
/** The keyboard height observer */
private KeyboardHeightObserver observer;
/** The cached landscape height of the keyboard */
private int keyboardLandscapeHeight;
/** The cached portrait height of the keyboard */
private int keyboardPortraitHeight;
/** The view that is used to calculate the keyboard height */
private View popupView;
/** The parent view */
private View parentView;
/** The root activity that uses this KeyboardHeightProvider */
private Activity activity;
/**
* Construct a new KeyboardHeightProvider
*
* @param activity The parent activity
*/
public KeyboardHeightProvider(Activity activity) {
super(activity);
this.activity = activity;
LayoutInflater inflator = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
this.popupView = inflator.inflate(R.layout.popupwindow, null, false);
setContentView(popupView);
setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
parentView = activity.findViewById(android.R.id.content);
setWidth(0);
setHeight(LayoutParams.MATCH_PARENT);
popupView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (popupView != null) {
handleOnGlobalLayout();
}
}
});
}
/**
* Start the KeyboardHeightProvider, this must be called after the onResume of the Activity.
* PopupWindows are not allowed to be registered before the onResume has finished
* of the Activity.
*/
public void start() {
if (!isShowing() && parentView.getWindowToken() != null) {
setBackgroundDrawable(new ColorDrawable(0));
showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
}
}
/**
* Close the keyboard height provider,
* this provider will not be used anymore.
*/
public void close() {
this.observer = null;
dismiss();
}
/**
* Set the keyboard height observer to this provider. The
* observer will be notified when the keyboard height has changed.
* For example when the keyboard is opened or closed.
*
* @param observer The observer to be added to this provider.
*/
public void setKeyboardHeightObserver(KeyboardHeightObserver observer) {
this.observer = observer;
}
/**
* Get the screen orientation
*
* @return the screen orientation
*/
private int getScreenOrientation() {
return activity.getResources().getConfiguration().orientation;
}
/**
* Popup window itself is as big as the window of the Activity.
* The keyboard can then be calculated by extracting the popup view bottom
* from the activity window height.
*/
private void handleOnGlobalLayout() {
Point screenSize = new Point();
activity.getWindowManager().getDefaultDisplay().getSize(screenSize);
Rect rect = new Rect();
popupView.getWindowVisibleDisplayFrame(rect);
// REMIND, you may like to change this using the fullscreen size of the phone
// and also using the status bar and navigation bar heights of the phone to calculate
// the keyboard height. But this worked fine on a Nexus.
int orientation = getScreenOrientation();
int keyboardHeight = screenSize.y - rect.bottom;
if (keyboardHeight == 0) {
notifyKeyboardHeightChanged(0, orientation);
}
else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
this.keyboardPortraitHeight = keyboardHeight;
notifyKeyboardHeightChanged(keyboardPortraitHeight, orientation);
}
else {
this.keyboardLandscapeHeight = keyboardHeight;
notifyKeyboardHeightChanged(keyboardLandscapeHeight, orientation);
}
}
/**
*
*/
private void notifyKeyboardHeightChanged(int height, int orientation) {
if (observer != null) {
observer.onKeyboardHeightChanged(height, orientation);
}
}
public interface KeyboardHeightObserver {
void onKeyboardHeightChanged(int height, int orientation);
}
}
popupwindow.xml
:
<?xml version="1.0" encoding="utf-8"?>
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/popuplayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:orientation="horizontal"/>
Використання в MainActivity
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
/**
* Created by nileshdeokar on 22/02/2018.
*/
class MainActivity : AppCompatActivity() , KeyboardHeightProvider.KeyboardHeightObserver {
private lateinit var keyboardHeightProvider : KeyboardHeightProvider
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
keyboardHeightProvider = KeyboardHeightProvider(this)
parentActivityView.post { keyboardHeightProvider?.start() }
}
override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
// In case of 18:9 - e.g. Samsung S8
// here you get the height of the navigation bar as negative value when keyboard is closed.
// and some positive integer when keyboard is opened.
}
public override fun onPause() {
super.onPause()
keyboardHeightProvider?.setKeyboardHeightObserver(null)
}
public override fun onResume() {
super.onResume()
keyboardHeightProvider?.setKeyboardHeightObserver(this)
}
public override fun onDestroy() {
super.onDestroy()
keyboardHeightProvider?.close()
}
}
Для будь-якої подальшої допомоги ви можете ознайомитись із розширеним використанням цього тут .
Просте однолінійне рішення
Як, наприклад, пропонується у багатьох вищезазначених відповідях
Просто отримати висоту смуги навігації може бути недостатньо. Нам потрібно врахувати, чи існує 1. панель навігації, 2. чи вона знаходиться внизу, або праворуч чи ліворуч, 3. чи програма відкрита в режимі з декількома вікнами.
На щастя, ви можете легко обійти все довге кодування, просто встановивши android:fitsSystemWindows="true"
в кореневому макеті. Система Android автоматично подбає про те, щоб додати до кореневого макета необхідні накладки, щоб переконатися, що дочірні подання не потрапляють у навігаційну смугу чи регіони стану.
Існує просте рішення з однієї лінії
android:fitsSystemWindows="true"
або програмно
findViewById(R.id.your_root_view).setFitsSystemWindows(true);
ви також можете отримати перегляд кореневих файлів від
findViewById(android.R.id.content).getRootView();
or
getWindow().getDecorView().findViewById(android.R.id.content)
Більш детальну інформацію про отримання кореневого перегляду див. На https://stackoverflow.com/a/4488149/9640177