Come ottengo l'altezza e la larghezza della barra di navigazione Android a livello di programmazione?


122

La barra di navigazione nera nella parte inferiore dello schermo non è facilmente rimovibile in Android. Fa parte di Android dalla 3.0 in sostituzione dei pulsanti hardware. Ecco una foto:

Barra di sistema

Come posso ottenere la dimensione della larghezza e l'altezza di questo elemento dell'interfaccia utente in pixel?


La migliore soluzione per questo problema viene aggiunta qui. Possiamo identificare la barra di navigazione presente nel dispositivo confrontando le metriche di visualizzazione e le metriche reali. Guarda la mia risposta, ho aggiunto il codice completo per scoprire le dimensioni effettive della barra di navigazione per interi dispositivi Android.
Sino Raj

Risposte:


178

Prova il codice seguente:

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

7
Grazie. +1. Sai quanto è stabile questo approccio? Gli identificatori delle risorse saranno soggetti a modifiche nelle diverse versioni della piattaforma?
Ben Pearson

59
Tieni presente che questa porzione di codice non restituisce 0 sui dispositivi senza barra di navigazione. Testato su Samsung S2 e S3. Ho ottenuto 72 e 96.
Egis

4
@Egidijus Guardate la mia risposta, verrà restituito 0 per i dispositivi che hanno la navigazione fisico stackoverflow.com/a/29938139/1683141
MDLC

1
anche galaxy s6 edge non restituisce 0, sebbene non sia presente una barra di navigazione in basso. restituisce 192.
agamov

5
Questo codice mostra la dimensione predefinita della barra di navigazione (prova anche navigation_bar_height_landscapeper l'altezza della barra di navigazione in modalità orizzontale e navigation_bar_widthper la larghezza di una barra di navigazione verticale). È necessario scoprire separatamente se e dove viene effettivamente visualizzata la barra di navigazione, ad esempio verificando la presenza di un pulsante di menu fisico. Forse puoi trovare un altro modo nel codice sorgente Android su android.googlesource.com/platform/frameworks/base/+/…
user149408

103

Ottengo le dimensioni della barra di navigazione confrontando le dimensioni dello schermo utilizzabile dall'app con le dimensioni reali dello schermo. Presumo che la barra di navigazione sia presente quando la dimensione dello schermo utilizzabile dall'app è inferiore alla dimensione dello schermo reale. Quindi calcolo le dimensioni della barra di navigazione. Questo metodo funziona con API 14 e versioni successive.

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

AGGIORNARE

Per una soluzione che tenga conto dei ritagli del display, controlla la risposta di John .


2
Questa è l'unica soluzione che funziona per me su tutti i diff di dispositivi che ho testato. Il mio requisito è determinare se la navBar si trova nella parte inferiore dello schermo tenendo conto dell'orientamento. Ho leggermente modificato il codice nella risposta per restituirmi la dimensione della barra di navigazione nella parte inferiore dello schermo, 0 indica che non è presente. I miei test mostrano che Nexus5 = portrait: 144, Landscape: 0 | Nexus7 = verticale: 96, orizzontale: 96 | Samsung Note II = Verticale: 0, Orizzontale: 0 | Samsung S10 = Verticale: 0, Orizzontale: 0
se22 come

2
Posso anche confermare che la soluzione di Danylo funziona solo su alcuni modelli. Questa soluzione di Egidijus è una soluzione di lavoro migliore e ha funzionato per tutti i modelli testati finora. Grazie per questo.
Bisclavret

2
La barra di stato fa parte della schermata utilizzabile dell'app.
Egis

1
Funziona, ma non considera se la barra esistente è nascosta o meno. Qualche idea su come controllare questo?
X-HuMan

1
Non funziona per Android P mentre il gesto di navigazione è attivo al posto della barra di navigazione. Puoi per favore guidarmi su come risolvere questa situazione
Nik

36

L'altezza di NavigationBar varia per alcuni dispositivi, ma anche per alcuni orientamenti. Per prima cosa devi controllare se il dispositivo ha una barra di navigazione, poi se il dispositivo è un tablet o un non-tablet (telefono) e infine devi guardare l'orientamento del dispositivo per ottenere l'altezza corretta.

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

Questo non funziona per me, Nexus5 restituisce true per hasMenuKey e hasBackKey quindi pensa che non ci sia navBar. (Nexus7 restituisce correttamente false per hasMenuKey e hasBackKey)
se22as

5
Ho testato questo codice sul Nexus 5 e restituisce false per entrambi. Sei sicuro di non essere su un emulatore o rom?
Mdlc

Sì, stavo usando un emulatore, scusa avrei dovuto chiarire nella mia dichiarazione in precedenza
se22as

non dovrebbe essere questo! hasMenuKey || ! hasBackKey invece di! hasMenuKey &&! hasBackKey? si prega di verificare
yongsunCN

2
Sfortunatamente, non funziona su Sony Xperia Z3 hasBackKey = false!anche se dovrebbe essere vero. boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
Hanno

27

In realtà la barra di navigazione sui tablet (almeno Nexus 7) ha dimensioni diverse in verticale e in orizzontale, quindi questa funzione dovrebbe assomigliare a questa:

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
Per recuperare l'orientamento dal contesto, utilizzarecontext.getResources().getConfiguration().orientation
Hugo Gresse

La migliore soluzione per questo problema viene aggiunta qui. Possiamo identificare la barra di navigazione presente nel dispositivo confrontando le metriche di visualizzazione e le metriche reali. Guarda la mia risposta, ho aggiunto il codice completo per scoprire le dimensioni effettive della barra di navigazione per interi dispositivi Android.
Sino Raj,

17

Penso che la risposta migliore sia qui perché ti consente di ottenere anche un'altezza di ritaglio uniforme.

Prendi la tua vista root e aggiungi setOnApplyWindowInsetsListener (oppure puoi sovrascrivere onApplyWindowInsets da essa) e prendi insets.getSystemWindowInsets da essa.

Nella mia attività della fotocamera, aggiungo padding uguale a systemWindowInsetBottom al mio layout inferiore. E infine, risolve il problema del ritaglio.

Inserti di attività della fotocamera

con appcompat è così

ViewCompat.setOnApplyWindowInsetsListener(mCameraSourcePreview, (v, insets) -> {
    takePictureLayout.setPadding(0,0,0,insets.getSystemWindowInsetBottom());
    return insets.consumeSystemWindowInsets();
});

senza appcompat, questo:

mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })

È possibile utilizzare setOnApplyWindowInsetsListenerinvece? Se é cosi, come? e perché hai differenziato LOLLIPOP e gli altri?
sviluppatore Android

sì, è possibile, ed è meglio. risolverò la risposta. e differenziare non è necessario troppo
Giovanni

1
Questa è la migliore risposta. Ti amo letteralmente.
Himanshu Rawat

1
Questa è la vera risposta.
nyconing

1
Il listener non viene mai eseguito quando lo chiamo da onCreate (). Mi sto perdendo qualcosa?
howettl

12

Spero che questo ti aiuta

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

Questo è il mio codice per aggiungere paddingRight e paddingBottom a una vista per schivare la barra di navigazione. Ho combinato alcune delle risposte qui e ho creato una clausola speciale per l'orientamento orizzontale insieme a isInMultiWindowMode. La chiave è leggere navigation_bar_height , ma anche controllare config_showNavigationBar per assicurarci che dovremmo effettivamente usare l'altezza.

Nessuna delle soluzioni precedenti ha funzionato per me. A partire da Android 7.0 devi prendere in considerazione la modalità Multi Window. Questo interrompe le implementazioni confrontando display.realSize con display.size poiché realSize ti dà le dimensioni dell'intero schermo (entrambe le finestre divise) e size ti dà solo le dimensioni della finestra dell'app. L'impostazione del padding su questa differenza lascerà l'intera visualizzazione come padding.

/** 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

L'altezza della barra di navigazione inferiore è 48dp (sia in modalità verticale che orizzontale) ed è 42dp quando la barra è posizionata verticalmente.


4
L'altezza è NORMALMENTE 48dp. Ma alcuni produttori possono cambiarlo, alcune ROM personalizzate possono cambiarlo e così via. È meglio fare affidamento sulla risposta di Danylo ( stackoverflow.com/a/26118045/1377145 ) per avere le dimensioni esatte
Hugo Gresse

È anche meglio. Grazie.
Aritra Roy

posizionato verticalmente? come quando è in modalità orizzontale?
sudocoder

1

La soluzione proposta da Egidijus e funziona perfettamente per Build.VERSION.SDK_INT> = 17

Ma ho ricevuto "NoSuchMethodException" durante l'esecuzione della seguente istruzione con Build.VERSION.SDK_INT <17 sul mio dispositivo:

Display.class.getMethod("getRawHeight").invoke(display);

Ho modificato il metodo getRealScreenSize () per questi casi:

else if(Build.VERSION.SDK_INT >= 14) 
{
    View decorView = getActivity().getWindow().getDecorView();
    size.x = decorView.getWidth();
    size.y = decorView.getHeight();
}

1

Ho risolto questo problema per tutti i dispositivi (inclusi Nexus 5, Samsung Galaxy Nexus 6 edge +, Samsung S10, Samsung Note II ecc.). Penso che questo ti aiuterà a gestire i problemi dipendenti dal dispositivo.

Qui sto aggiungendo due tipi di codici,

Codice Java (per Android nativo):

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

E codice C # (per 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()richiede il livello API 17
Weizhi

se (Build.VERSION.SdkInt> = BuildVersionCodes.JellyBeanMr2) è già selezionato.
Sino Raj

Il codice deve essere risolto. Il mio LG Nexus 5X otterrà 0,0 a causa di: [1] Quando screenOrientationè l'impostazione predefinita, hasPermanentMenuKeyè falsesolo se non ruota. [2] Quando screenOrientationè paesaggio, cade nell displayMetrics.heightPixels != realDisplayMetrics.heightPixels)'altro. [3] Quando screenOrientationè verticale, hasPermanentMenuKeyè false.
Frutta

funziona, ma su 4.x se (hasPermanentMenuKey) è spreco, commentalo
djdance

La tua condizione dovrebbe essere if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1). L'API 17 è richiesta per eseguire public void getRealMetrics (DisplayMetrics outMetrics). Consulta la documentazione: developer.android.com/reference/kotlin/android/util/…
portfoliobuilder

1

Combinando la risposta di @egis e altri: funziona bene su una varietà di dispositivi, testato su Pixel EMU, Samsung S6, Sony Z3, Nexus 4. Questo codice utilizza le dimensioni del display per verificare la disponibilità della barra di navigazione e quindi utilizza l'effettiva dimensione della barra di navigazione del sistema se presente.

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

}


Se voglio impostare a livello di programmazione l'altezza di un TextView nella parte inferiore dell'altezza utilizzabile dello schermo, come dovrei usare questa classe?
Naveed Abbas

1

Come ottenere l'altezza della barra di navigazione e della barra di stato. Questo codice funziona per me su alcuni dispositivi Huawei e Samsung . La soluzione di Egis sopra è buona, tuttavia, è ancora errata su alcuni dispositivi. Quindi, l'ho migliorato.

Questo è il codice per ottenere l'altezza della barra di stato

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
    }

Questo metodo restituisce sempre l'altezza della barra di navigazione anche quando la barra di navigazione è nascosta.

private fun getNavigationBarHeight(resources: Resources): Int {
    val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
    return if (resourceId > 0) {
        resources.getDimensionPixelSize(resourceId)
    } else 0
}

NOTA: su Samsung A70, questo metodo restituisce l'altezza della barra di stato + l'altezza della barra di navigazione. Su altri dispositivi (Huawei), restituisce solo l'altezza della barra di navigazione e restituisce 0 quando la barra di navigazione è nascosta.

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

Questo è il codice per ottenere l'altezza della barra di navigazione e della barra di stato

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

L'ho fatto, funziona su tutti i dispositivi che ho testato e anche sugli emulatori:

// 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

Ecco come l'ho risolto. Ho creato una barra inferiore nascosta che necessitava di imbottitura a seconda che ci fosse o meno una barra di navigazione (capacitiva, sullo schermo o solo pre lollipop).


Visualizza

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

Nel mio caso in cui volevo avere qualcosa del genere:

Immagine dello schermo

Ho dovuto seguire la stessa cosa suggerita da @Mdlc ma probabilmente leggermente più semplice (targeting solo > = 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)

Funziona anche sul paesaggio:

paesaggio

Modifica La soluzione di cui sopra non funziona correttamente in modalità multi-finestra in cui il rettangolo utilizzabile non è più piccolo solo a causa della barra di navigazione ma anche a causa della dimensione della finestra personalizzata. Una cosa che ho notato è che in multi-finestra la barra di navigazione non è posizionata sull'app, quindi anche senza modifiche al padding di DecorView abbiamo il comportamento corretto:

Multifinestra senza modifiche all'imbottitura della vista del decoro Finestra singola senza modifiche all'imbottitura della vista del decoro

Nota la differenza tra il modo in cui la barra di navigazione passa sopra la parte inferiore dell'app in questi scenari. Fortunatamente, questo è facile da risolvere. Possiamo verificare se l'app è multi finestra. Il codice seguente include anche la parte per calcolare e regolare la posizione della barra degli strumenti (soluzione completa: 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
    }

Risultato in modalità popup Samsung:

inserisci qui la descrizione dell'immagine


0

Codice testato per ottenere l'altezza della barra di navigazione (in pixel):

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

Codice testato per ottenere l'altezza della barra di stato (in pixel):

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

Conversione di pixel in dp:

public static int pxToDp(int px) {
    return (int) (px / Resources.getSystem().getDisplayMetrics().density);
}

0

Nel caso di Samsung S8 nessuno dei metodi sopra forniti forniva l'altezza corretta della barra di navigazione, quindi ho usato il provider di altezza della tastiera KeyboardHeightProvider android . E mi ha dato l'altezza in valori negativi e per il posizionamento del layout ho regolato quel valore nei calcoli.

Ecco 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"/>

Utilizzo in 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()
    }
}

Per qualsiasi ulteriore aiuto puoi dare un'occhiata all'uso avanzato di questo qui .


0

Soluzione su una riga semplice

Come suggerito in molte delle risposte precedenti, per esempio

Ottenere semplicemente l'altezza della barra di navigazione potrebbe non essere sufficiente. Dobbiamo considerare se 1. la barra di navigazione esiste, 2. è in basso, oppure a destra oa sinistra, 3. l'app è aperta in modalità multi-finestra.

Fortunatamente puoi facilmente bypassare tutta la lunga codifica semplicemente impostandola android:fitsSystemWindows="true"nel layout di root. Il sistema Android si occuperà automaticamente di aggiungere il riempimento necessario al layout radice per assicurarsi che le visualizzazioni figlio non entrino nella barra di navigazione o nelle regioni della barra di stato.

C'è una semplice soluzione di una riga

android:fitsSystemWindows="true"

o programmaticamente

findViewById(R.id.your_root_view).setFitsSystemWindows(true);

puoi anche ottenere la vista root da

findViewById(android.R.id.content).getRootView();
or
getWindow().getDecorView().findViewById(android.R.id.content)

Per maggiori dettagli su come ottenere la visualizzazione root, fare riferimento a: https://stackoverflow.com/a/4488149/9640177

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.