Як я можу програмно отримати висоту та ширину навігаційної панелі Android?


122

Чорну смугу навігації в нижній частині екрана в Android не можна легко зняти. Він є частиною Android з 3.0, як заміна апаратних кнопок. Ось малюнок:

Системний рядок

Як я можу отримати розмір ширини та висоти цього елемента інтерфейсу у пікселях?


Тут найкраще вирішити цю проблему. Ми можемо визначити, чи є пристрій навігаційної панелі на пристрої, порівнюючи метрики відображення та реальні показники. Подивіться мою відповідь, я додав повний код, щоб дізнатися фактичний розмір панелі навігації для цілих Android-пристроїв.
Sino Raj

Відповіді:


178

Спробуйте нижче код:

Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
    return resources.getDimensionPixelSize(resourceId);
}
return 0;

7
Дякую. +1. Чи знаєте ви, наскільки стійкий такий підхід? Чи будуть ідентифікатори ресурсів змінюватися в різних версіях платформи?
Бен Пірсон

59
Зауважте, що цей фрагмент коду не повертає 0 на пристроях без панелі навігації. Тестували на Samsung S2 та S3. Мені було 72 і 96.
Егіс

4
@Egidijus Подивіться на мою відповідь, він повертає 0 для пристроїв , які мають фізичну навігаційний stackoverflow.com/a/29938139/1683141
MDLC

1
край галактики s6 також не повертає 0, хоча внизу немає панелі навігації. повертає 192.
агамов

5
Цей код показує розмір навігаційної панелі за замовчуванням (спробуйте також navigation_bar_height_landscapeвисоту панелі навігації в ландшафтному режимі та navigation_bar_widthширину вертикальної панелі навігації). Ви повинні дізнатися окремо, чи дійсно відображається панель навігації, наприклад, перевіривши наявність фізичної кнопки меню. Можливо, ви можете знайти інший спосіб у коді Android
souce за

103

Я отримую розмір панелі навігації, порівнюючи розмір екрана, застосований для додатків, з реальним розміром екрана. Я припускаю, що панель навігації присутня, коли розмір екрана, який можна застосувати, менший за розмір реального екрана. Потім я обчислюю розмір навігаційної панелі. Цей метод працює з 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;
}

ОНОВЛЕННЯ

Для рішення, яке враховує вирізи на екрані, перевірте відповідь Джона .


2
Це єдине рішення, яке працює для мене на всіх пристроях, які я тестував. Мій запит - визначити, чи знаходиться navBar внизу екрана з урахуванням орієнтації. Я трохи змінив код у відповіді, щоб просто повернути мені розмір панелі навігації в нижній частині екрана, 0 вказує на його відсутність. Моє тестування показує, що Nexus5 = портрет: 144, Пейзаж: 0 | Nexus7 = портрет: 96, Пейзаж: 96 | Samsung Note II = портрет: 0, пейзаж: 0 | Samsung S10 = портрет: 0, краєвид: 0
se22as

2
Я також можу підтвердити, що рішення Данила працює лише на деяких моделях. Це рішення Egidijus є кращим робочим рішенням, і воно працювало для всіх моделей, протестованих до цього часу. Дякую за це.
Біскларет

2
Рядок стану є частиною екрана, який можна застосувати.
Егіс

1
Це працює, але не враховує, чи наявна смужка прихована чи ні. Будь-яка ідея, як це перевірити?
X-HuMan

1
Він не працює для Android P, поки увімкнено навігаційний жест замість панелі навігації. Чи можете ви
Нік

36

Висота 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;
}

Це не працює для мене, Nexus5 повертає true для hasMenuKey та hasBackKey, тому він вважає, що немає navBar. (Nexus7 правильно повертає false для hasMenuKey та hasBackKey)
se22as

5
Я перевірив цей код на Nexus 5, і він повертає помилку для обох. Ви впевнені, що не на емуляторі чи ромі?
Mdlc

Так, я використовував емулятор, вибачте, я повинен був уточнити в своїй заяві раніше
se22as

це не повинно бути! hasMenuKey || ! hasBackKey замість! hasMenuKey &&! hasBackKey? будь ласка, перевірте
yongsunCN

2
На жаль, на Sony Xperia Z3 не працює, hasBackKey = false!хоча це має бути правдою. Натомість працювали наступні: boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
Hamzeh Soboh

27

Насправді панель навігації на планшетах (принаймні 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;
}

6
Щоб вивести орієнтацію з контексту, використовуйтеcontext.getResources().getConfiguration().orientation
Hugo Gresse

Тут найкраще вирішити цю проблему. Ми можемо визначити, чи є пристрій навігаційної панелі на пристрої, порівнюючи метрики відображення та реальні показники. Подивіться мою відповідь, я додав повний код, щоб дізнатися фактичний розмір панелі навігації для цілих Android-пристроїв.
Sino Raj

17

Я думаю, що краща відповідь є тут, оскільки це дозволяє вам отримати рівну висоту вирізу.

Виберіть свій кореневий вигляд і додайте 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 та решту?
андроїд розробник

так, можливо, і краще. я виправлю відповідь. і розрізняти теж не потрібно
Джон

1
Це найкраща відповідь. Я буквально тебе люблю.
Хіманшу Рават

1
Це справжня відповідь.
nyconing

1
Слухач ніколи не виконується, коли я викликаю це від onCreate (). Я щось пропускаю?
howettl

12

Я сподіваюся, що це вам допоможе

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;
}

5

Це мій код, щоб додати 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);
}

1

Висота нижньої панелі навігації - 48 дп (як в портретному, так і в пейзажному режимі) і становить 42 дп при розміщенні планки вертикально.


4
Висота НОРМАЛЬНО 48dp. Але деякі виробники можуть це змінити, деякі користувальницькі ROM можуть це змінити тощо. Краще покластися на відповідь Данила ( stackoverflow.com/a/26118045/1377145 ), щоб мати точний розмір
Уго Гресс

Це ще краще. Дякую.
Арітра Рой

розміщені вертикально? наприклад, коли це в ландшафтному режимі?
судокодер

1

Рішення, запропоноване 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();
}

1

Я вирішив цю проблему для всіх пристроїв (включаючи 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
Weizhi

якщо (Build.VERSION.SdkInt> = BuildVersionCodes.JellyBeanMr2) вже перевірено.
Sino Raj

Код потрібно виправити. Мій LG Nexus 5X отримає 0,0 за рахунок: [1] Якщо screenOrientationза замовчуванням, hasPermanentMenuKeyце falseлише якщо не обертається. [2] Коли screenOrientationпейзаж, він потрапляє в displayMetrics.heightPixels != realDisplayMetrics.heightPixels)інше місце. [3] Коли screenOrientationпортрет, hasPermanentMenuKeyє false.
Фрукти

це працює, але на 4.x, якщо (hasPermanentMenuKey) є сміттям, прокоментуйте це
djdance

Ваша умова повинна бути, якщо (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1). API 17 потрібен для того, щоб робити публічну недійсність getRealMetrics (DisplayMetrics outMetrics). Дивіться документи: developer.android.com/reference/kotlin/android/util/…
portfoliobuilder

1

Поєднання відповіді від @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);
	}

}


Якщо я хочу програмно встановити висоту TextView в нижній частині корисної висоти екрана, як би я використовував цей клас?
Naveed Abbas

1

Як отримати висоту панелі навігації та рядка стану. Цей код працює для мене на деяких пристроях 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
                }

1

Я це зробив, він працює на кожному тестованому пристрої і навіть на емуляторах:

// 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());
}

0

Ось як я це вирішив. Я зробив приховану нижню панель, яка потребувала набивання залежно від того, була панель навігації чи ні (ємнісний, на екрані чи просто попередній льодяник).


Вид

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;
}

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:

введіть тут опис зображення


0

Тестований код для отримання висоти панелі навігації (у пікселях):

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

0

У випадку 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()
    }
}

Для будь-якої подальшої допомоги ви можете ознайомитись із розширеним використанням цього тут .


0

Просте однолінійне рішення

Як, наприклад, пропонується у багатьох вищезазначених відповідях

Просто отримати висоту смуги навігації може бути недостатньо. Нам потрібно врахувати, чи існує 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.