Comment obtenir la hauteur et la largeur de la barre de navigation Android par programmation?

122

La barre de navigation noire en bas de l'écran n'est pas facilement amovible sous Android. Il fait partie d'Android depuis 3.0 en remplacement des boutons matériels. Voici une image:

Barre système

Comment puis-je obtenir la taille de la largeur et la hauteur de cet élément d'interface utilisateur en pixels?

Kevik
la source
La meilleure solution à ce problème est ajoutée ici. Nous pouvons identifier la barre de navigation présente dans l'appareil en comparant les mesures d'affichage et les mesures réelles. Regardez ma réponse, j'ai ajouté le code complet pour connaître la taille réelle de la barre de navigation pour les appareils Android entiers.
Sino Raj

Réponses:

178

Essayez le code ci-dessous:

Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
    return resources.getDimensionPixelSize(resourceId);
}
return 0;
Sanket
la source
7
Merci. +1. Savez-vous à quel point cette approche est stable? Les identificateurs de ressources seront-ils susceptibles de changer selon les différentes versions de la plate-forme?
Ben Pearson
59
Notez que ce morceau de code ne renvoie pas 0 sur les appareils sans barre de navigation. Testé sur Samsung S2 et S3. J'ai 72 et 96.
Egis
4
@Egidijus Regardez ma réponse, elle renverra 0 pour les appareils qui ont une navigation physique stackoverflow.com/a/29938139/1683141
Mdlc
1
galaxy s6 edge ne renvoie pas non plus 0, bien qu'il n'y ait pas de barre de navigation en bas. renvoie 192.
agamov
5
Ce code montre la taille par défaut de la barre de navigation (essayez également navigation_bar_height_landscapepour la hauteur de la barre de navigation en mode paysage et navigation_bar_widthpour la largeur d'une barre de navigation verticale). Vous devez savoir séparément si et où la barre de navigation s'affiche réellement, par exemple en testant la présence d'un bouton de menu physique. Peut-être que vous pouvez trouver un autre moyen dans le code de souce Android à android.googlesource.com/platform/frameworks/base
+
103

J'obtiens la taille de la barre de navigation en comparant la taille d'écran utilisable par l'application avec la taille réelle de l'écran. Je suppose que la barre de navigation est présente lorsque la taille d'écran utilisable par l'application est inférieure à la taille réelle de l'écran. Ensuite, je calcule la taille de la barre de navigation. Cette méthode fonctionne avec l'API 14 et plus.

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

METTRE À JOUR

Pour une solution qui prend en compte les découpes d'affichage, veuillez consulter la réponse de John .

Égide
la source
2
C'est la seule solution qui fonctionne pour moi sur tous les différents appareils que j'ai testés. Ma demande est de déterminer si la barre de navigation est en bas de l'écran en tenant compte de l'orientation. J'ai légèrement modifié le code dans la réponse pour simplement me renvoyer la taille de la barre de navigation en bas de l'écran, 0 indique qu'elle n'est pas présente. Mes tests montrent que Nexus5 = portrait: 144, paysage: 0 | Nexus7 = portrait: 96, paysage: 96 | Samsung Note II = portrait: 0, paysage: 0 | Samsung S10 = portrait: 0, Paysage: 0
se22as
2
Je peux également confirmer que la solution de Danylo ne fonctionne que sur certains modèles. Cette solution d'Egidijus est une meilleure solution de travail, et a fonctionné pour tous les modèles testés jusqu'à présent. Merci pour ça.
Bisclavret
2
La barre d'état fait partie de l'écran utilisable de l'application.
Egis
1
Cela fonctionne, mais ne prend pas en compte si la barre existante est masquée ou non. Une idée comment vérifier cela?
X-HuMan
1
Cela ne fonctionne pas pour Android P lorsque le geste de navigation est activé au lieu de la barre de navigation. Pouvez-vous s'il vous plaît me guider comment résoudre cette situation
Nik
36

La hauteur de la barre de navigation varie pour certains appareils, mais également pour certaines orientations. Vous devez d'abord vérifier si l'appareil dispose d'une barre de navigation, puis si l'appareil est une tablette ou une non-tablette (téléphone) et enfin vous devez regarder l'orientation de l'appareil afin d'obtenir la bonne hauteur.

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;
}
Mdlc
la source
Cela ne fonctionne pas pour moi, Nexus5 retourne true pour hasMenuKey et hasBackKey donc il pense qu'il n'y a pas de navBar. (Nexus7 renvoie correctement false pour hasMenuKey et hasBackKey)
se22as
5
J'ai testé ce code sur le Nexus 5, et il renvoie false pour les deux. Êtes-vous sûr que vous n'êtes pas sur un émulateur ou une rom?
Mdlc
Oui,
j'utilisais
ça ne devrait pas être! hasMenuKey || ! hasBackKey au lieu de! hasMenuKey &&! hasBackKey? s'il vous plaît vérifier
yongsunCN
2
Malheureusement, ne fonctionne pas sur Sony Xperia Z3 hasBackKey = false!même si cela devrait être vrai. Ce qui suit a fonctionné à la place: boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
Hamzeh Soboh
27

En fait, la barre de navigation sur les tablettes (au moins Nexus 7) a une taille différente en portrait et en paysage, donc cette fonction devrait ressembler à ceci:

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;
}
Danylo Volokh
la source
6
Pour récupérer l'orientation à partir du contexte, utilisezcontext.getResources().getConfiguration().orientation
Hugo Gresse
La meilleure solution à ce problème est ajoutée ici. Nous pouvons identifier la barre de navigation présente dans l'appareil en comparant les mesures d'affichage et les mesures réelles. Regardez ma réponse, j'ai ajouté le code complet pour connaître la taille réelle de la barre de navigation pour les appareils Android entiers.
Sino Raj
17

Je pense que la meilleure réponse est ici car elle vous permet également d'obtenir une hauteur de découpe uniforme.

Prenez votre vue racine et ajoutez setOnApplyWindowInsetsListener (ou vous pouvez remplacer onApplyWindowInsets à partir de celle-ci) et en extraire insets.getSystemWindowInsets.

Dans mon activité de caméra, j'ajoute un rembourrage égal au systemWindowInsetBottom à ma disposition inférieure. Et enfin, il corrige le problème de découpe.

Inserts d'activité de la caméra

avec appcompat c'est comme ça

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

sans appcompat, ceci:

mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })
John
la source
Est-il possible d'utiliser à la setOnApplyWindowInsetsListenerplace? Si c'est le cas, comment? et pourquoi avez-vous fait la différence entre LOLLIPOP et les autres?
développeur android
oui, c'est possible et c'est mieux. je vais réparer la réponse. et différencier n'ont pas besoin de trop
John
1
C'est la meilleure réponse. Je t'aime littéralement.
Himanshu Rawat
1
C'est la vraie réponse.
nyconing
1
L'écouteur n'est jamais exécuté lorsque j'appelle cela depuis onCreate (). Est-ce que je manque quelque chose?
howettl
12

J'espère que ceci vous aide

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;
}
Francisco Drumond
la source
5

Ceci est mon code pour ajouter paddingRight et paddingBottom à une vue pour éviter la barre de navigation. J'ai combiné certaines des réponses ici et fait une clause spéciale pour l'orientation paysage avec isInMultiWindowMode. La clé est de lire navigation_bar_height , mais aussi de vérifier config_showNavigationBar pour s'assurer que nous devrions réellement utiliser la hauteur.

Aucune des solutions précédentes n'a fonctionné pour moi. Depuis Android 7.0, vous devez prendre en compte le mode multi-fenêtres. Cela rompt avec les implémentations comparant display.realSize avec display.size depuis realSize vous donne les dimensions de l'écran entier ( les deux fenêtres split) et la taille ne vous donne les dimensions de votre fenêtre App. Si vous définissez le remplissage sur cette différence, votre vue entière sera remplie.

/** 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);
}
Marcus
la source
1

La hauteur de la barre de navigation inférieure est de 48dp (en mode portrait et paysage) et est de 42dp lorsque la barre est placée verticalement.

Aritra Roy
la source
4
La hauteur est NORMALEMENT 48dp. Mais certains fabricants peuvent changer cela, certaines ROM personnalisées peuvent changer cela, et ainsi de suite. Il vaut mieux se fier à la réponse de Danylo ( stackoverflow.com/a/26118045/1377145 ) pour avoir la taille exacte
Hugo Gresse
C'est même mieux. Merci.
Aritra Roy
placé verticalement? comme quand il est en mode paysage?
sudocoder
1

La solution proposée par Egidijus et fonctionne parfaitement pour Build.VERSION.SDK_INT> = 17

Mais j'ai eu "NoSuchMethodException" lors de l'exécution de l'instruction suivante avec Build.VERSION.SDK_INT <17 sur mon appareil:

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

J'ai modifié la méthode getRealScreenSize () pour de tels cas:

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

J'ai résolu ce problème pour tous les appareils (y compris Nexus 5, Samsung Galaxy Nexus 6 edge +, Samsung S10, Samsung Note II, etc.). Je pense que cela vous aidera à gérer les problèmes liés à l'appareil.

Ici, j'ajoute deux types de codes,

Code Java (pour Android natif):

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

Et code C # (pour 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;
        }
Sino Raj
la source
Display.getRealMetrics()nécessite le niveau d'API 17
Weizhi
si (Build.VERSION.SdkInt> = BuildVersionCodes.JellyBeanMr2) déjà coché.
Sino Raj
Le code doit être corrigé. Mon LG Nexus 5X obtiendra 0.0 en raison de: [1] Quand screenOrientationest par défaut, hasPermanentMenuKeyc'est falseseulement s'il ne tourne pas. [2] Quand screenOrientationest paysage, il tombe dans displayMetrics.heightPixels != realDisplayMetrics.heightPixels)l'autre. [3] Quand screenOrientationest portrait, hasPermanentMenuKeyest false.
fruits
cela fonctionne, mais sur 4.x si (hasPermanentMenuKey) est du gaspillage, commentez-le
djdance
Votre condition doit être if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1). L'API 17 est requise pour effectuer une opération publique void getRealMetrics (DisplayMetrics outMetrics). Voir la documentation: developer.android.com/reference/kotlin/android/util/…
portfoliobuilder
1

Combinant la réponse de @egis et d'autres - cela fonctionne bien sur une variété d'appareils, testés sur Pixel EMU, Samsung S6, Sony Z3, Nexus 4. Ce code utilise les dimensions de l'écran pour tester la disponibilité de la barre de navigation, puis utilise le réel la taille de la barre de navigation du système, le cas échéant.

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

}

Méchant homme
la source
Si je veux définir par programme la hauteur d'un TextView au bas de la hauteur utilisable de l'écran, comment utiliserais-je cette classe?
Naveed Abbas
1

Comment obtenir la hauteur de la barre de navigation et de la barre d'état. Ce code fonctionne pour moi sur certains appareils Huawei et Samsung . La solution Egis ci-dessus est bonne, cependant, elle est toujours incorrecte sur certains appareils. Alors, je l'ai amélioré.

Ceci est du code pour obtenir la hauteur de la barre d'état

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
    }

Cette méthode renvoie toujours la hauteur de la barre de navigation même lorsque la barre de navigation est masquée.

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

REMARQUE: sur Samsung A70, cette méthode renvoie la hauteur de la barre d'état + la hauteur de la barre de navigation. Sur les autres appareils (Huawei), il ne renvoie que la hauteur de la barre de navigation et renvoie 0 lorsque la barre de navigation est masquée.

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

Ceci est le code pour obtenir la hauteur de la barre de navigation et de la barre d'état

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
                }
Anh Duy
la source
1

J'ai fait cela, cela fonctionne sur tous les appareils que j'ai testés, et même sur les émulateurs:

// 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());
}
Matteo
la source
0

Voici comment j'ai résolu ce problème. J'ai fait une barre inférieure masquable qui nécessitait un rembourrage selon qu'il y avait une barre de navigation ou non (capacitive, à l'écran ou juste pré sucette).


Vue

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;
}
Jonas Borggren
la source
0

Dans mon cas où je voulais avoir quelque chose comme ça:

Capture d'écran

J'ai dû suivre la même chose que suggérée par @Mdlc mais probablement un peu plus simple (ciblage uniquement > = 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)

Cela fonctionne aussi sur le paysage:

paysage

Modifier La solution ci-dessus ne fonctionne pas correctement en mode multi-fenêtres où le rectangle utilisable n'est pas plus petit uniquement en raison de la barre de navigation mais également en raison de la taille de la fenêtre personnalisée. Une chose que j'ai remarquée est que dans les fenêtres multiples, la barre de navigation ne survole pas l'application, donc même sans modification du rembourrage DecorView, nous avons le bon comportement:

Fenêtres multiples sans modification du remplissage de la vue décorative Fenêtre unique sans modification du rembourrage de la vue du décor

Notez la différence entre la façon dont la barre de navigation survole le bas de l'application dans ces scénarios. Heureusement, cela est facile à résoudre. Nous pouvons vérifier si l'application est multi-fenêtre. Le code ci-dessous comprend également la partie pour calculer et ajuster la position de la barre d'outils (solution complète: 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
    }

Résultat en mode popup Samsung:

entrez la description de l'image ici

Bakhshi
la source
0

Code testé pour obtenir la hauteur de la barre de navigation (en pixels):

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

Code testé pour obtenir la hauteur de la barre d'état (en pixels):

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

Conversion de pixels en dp:

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

Dans le cas de Samsung S8, aucune des méthodes fournies ci-dessus ne donnait une hauteur appropriée de la barre de navigation, j'ai donc utilisé le fournisseur de hauteur de clavier KeyboardHeightProvider android . Et cela m'a donné de la hauteur en valeurs négatives et pour le positionnement de ma mise en page, j'ai ajusté cette valeur dans les calculs.

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

Utilisation en 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()
    }
}

Pour toute aide supplémentaire, vous pouvez consulter l'utilisation avancée de ceci ici .

Nilesh Deokar
la source
0

Solution simple en une ligne

Comme suggéré dans de nombreuses réponses ci-dessus, par exemple

Obtenir simplement la hauteur de la barre de navigation peut ne pas être suffisant. Nous devons déterminer si 1. la barre de navigation existe, 2. est-elle en bas, ou à droite ou à gauche, 3. l'application est-elle ouverte en mode multi-fenêtres.

Heureusement, vous pouvez facilement contourner tout le codage long en définissant simplement android:fitsSystemWindows="true"votre disposition racine. Le système Android se chargera automatiquement d'ajouter le remplissage nécessaire à la disposition racine pour s'assurer que les vues enfants n'entrent pas dans la barre de navigation ou les régions de la barre d'état.

Il existe une solution simple en une seule ligne

android:fitsSystemWindows="true"

ou par programme

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

vous pouvez également obtenir une vue racine en

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

Pour plus de détails sur l'obtention de la vue racine, reportez-vous à https://stackoverflow.com/a/4488149/9640177

mayank1513
la source