Wie erhalte ich programmgesteuert die Höhe und Breite der Android-Navigationsleiste?

122

Die schwarze Navigationsleiste am unteren Bildschirmrand kann unter Android nicht einfach entfernt werden. Es ist seit 3.0 Teil von Android als Ersatz für Hardwaretasten. Hier ist ein Bild:

Systemleiste

Wie kann ich die Größe der Breite und die Höhe dieses UI-Elements in Pixel ermitteln?

Kevik
quelle
Die beste Lösung für dieses Problem finden Sie hier. Wir können feststellen, ob eine Navigationsleiste im Gerät vorhanden ist, indem wir Anzeigemetriken und reale Metriken vergleichen. Schauen Sie sich meine Antwort an. Ich habe den vollständigen Code hinzugefügt, um die tatsächliche Größe der Navigationsleiste für ganze Android-Geräte zu ermitteln.
Sino Raj

Antworten:

178

Versuchen Sie den folgenden Code:

Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
    return resources.getDimensionPixelSize(resourceId);
}
return 0;
Sanket
quelle
7
Vielen Dank. +1. Wissen Sie, wie stabil dieser Ansatz ist? Werden sich die Ressourcenkennungen in verschiedenen Plattformversionen ändern können?
Ben Pearson
59
Beachten Sie, dass dieser Code auf Geräten ohne Navigationsleiste keine 0 zurückgibt. Getestet auf Samsung S2 und S3. Ich habe 72 und 96.
Egis
4
@Egidijus Schauen Sie sich meine Antwort an, es wird 0 für Geräte zurückgeben, die physische Navigation haben stackoverflow.com/a/29938139/1683141
Mdlc
1
galaxy s6 edge gibt auch nicht 0 zurück, obwohl es unten keine Navigationsleiste gibt. kehrt 192 zurück.
Agamov
5
Dieser Code zeigt die Standardgröße der Navigationsleiste an (versuchen Sie auch die navigation_bar_height_landscapeHöhe der Navigationsleiste im Querformat und navigation_bar_widthdie Breite einer vertikalen Navigationsleiste). Sie müssen separat herausfinden, ob und wo die Navigationsleiste tatsächlich angezeigt wird, z. B. indem Sie prüfen, ob eine physische Menüschaltfläche vorhanden ist. Vielleicht finden Sie einen anderen Weg im Android-Quellcode unter android.googlesource.com/platform/frameworks/base/+/…
user149408
103

Ich erhalte die Größe der Navigationsleiste, indem ich die von der App verwendbare Bildschirmgröße mit der tatsächlichen Bildschirmgröße vergleiche. Ich gehe davon aus, dass die Navigationsleiste vorhanden ist, wenn die von der App verwendbare Bildschirmgröße kleiner als die tatsächliche Bildschirmgröße ist. Dann berechne ich die Größe der Navigationsleiste. Diese Methode funktioniert mit API 14 und höher.

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

AKTUALISIEREN

Für eine Lösung, die Anzeigeausschnitte berücksichtigt, überprüfen Sie bitte Johns Antwort .

Egis
quelle
2
Dies ist die einzige Lösung, die für mich auf allen verschiedenen Geräten funktioniert, die ich getestet habe. Meine Anforderung besteht darin, unter Berücksichtigung der Ausrichtung festzustellen, ob sich die Navigationsleiste am unteren Bildschirmrand befindet. Ich habe den Code in der Antwort leicht angepasst, um mir nur die Größe der Navigationsleiste am unteren Bildschirmrand zurückzugeben. 0 zeigt an, dass sie nicht vorhanden ist. Meine Tests zeigen, dass Nexus5 = Hochformat: 144, Querformat: 0 | Nexus7 = Hochformat: 96, Querformat: 96 | Samsung Note II = Hochformat: 0, Querformat: 0 | Samsung S10 = Porträt: 0, Landschaft: 0
se22as
2
Ich kann auch bestätigen, dass die Lösung von Danylo nur bei einigen Modellen funktioniert. Diese Lösung von Egidijus ist eine besser funktionierende Lösung und hat für alle bisher getesteten Modelle funktioniert. Danke dafür.
Bisclavret
2
Die Statusleiste ist Teil des von der App verwendbaren Bildschirms.
Egis
1
Dies funktioniert, berücksichtigt jedoch nicht, ob die vorhandene Leiste ausgeblendet ist oder nicht. Irgendeine Idee, wie man das überprüft?
X-HuMan
1
Es funktioniert nicht für Android P, während die Navigationsgeste anstelle der Navigationsleiste aktiviert ist. Können Sie mir bitte zeigen, wie ich diese Situation lösen kann
Nik
36

Die Höhe der Navigationsleiste variiert für einige Geräte, aber auch für einige Ausrichtungen. Zuerst müssen Sie überprüfen, ob das Gerät über eine Navigationsleiste verfügt, dann, ob es sich bei dem Gerät um ein Tablet oder ein Nicht-Tablet (Telefon) handelt, und schließlich müssen Sie die Ausrichtung des Geräts überprüfen, um die richtige Höhe zu erhalten.

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
quelle
Dies funktioniert bei mir nicht, Nexus5 gibt true für hasMenuKey und hasBackKey zurück und glaubt daher, dass es keine navBar gibt. (Nexus7 gibt korrekt false für hasMenuKey und hasBackKey zurück)
se22as
5
Ich habe diesen Code auf dem Nexus 5 getestet und er gibt für beide false zurück. Bist du sicher, dass du nicht auf einem Emulator oder einer Rom bist?
Mdlc
Ja, ich habe einen Emulator verwendet, sorry, ich hätte es in meiner Erklärung vorher
klarstellen sollen
sollte das nicht sein! hasMenuKey || ! hasBackKey statt! hasMenuKey &&! hasBackKey? Bitte überprüfen Sie
yongsunCN
2
Funktioniert leider nicht auf Sony Xperia Z3, hasBackKey = false!obwohl es wahr sein sollte. Folgendes funktionierte stattdessen: boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
Hamzeh Soboh
26

Tatsächlich hat die Navigationsleiste auf Tablets (mindestens Nexus 7) im Hoch- und Querformat unterschiedliche Größen, sodass diese Funktion folgendermaßen aussehen sollte:

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
quelle
6
Verwenden Siecontext.getResources().getConfiguration().orientation
Hugo Gresse
Die beste Lösung für dieses Problem finden Sie hier. Wir können feststellen, ob eine Navigationsleiste im Gerät vorhanden ist, indem wir Anzeigemetriken und reale Metriken vergleichen. Schauen Sie sich meine Antwort an. Ich habe den vollständigen Code hinzugefügt, um die tatsächliche Größe der Navigationsleiste für ganze Android-Geräte zu ermitteln.
Sino Raj
17

Ich denke, eine bessere Antwort ist hier, weil Sie damit auch eine gleichmäßige Ausschnitthöhe erzielen können.

Nehmen Sie Ihre Stammansicht und fügen Sie setOnApplyWindowInsetsListener hinzu (oder Sie können onApplyWindowInsets daraus überschreiben) und nehmen Sie insets.getSystemWindowInsets daraus.

In meiner Kameraaktivität füge ich meinem unteren Layout eine Auffüllung hinzu, die dem systemWindowInsetBottom entspricht. Und schließlich wurde das Problem mit dem Ausschnitt behoben.

Kameraaktivitätseinsätze

mit appcompat ist es so

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

ohne appcompat:

mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })
John
quelle
Ist es möglich, setOnApplyWindowInsetsListenerstattdessen zu verwenden ? Wenn das so ist, wie? und warum hast du zwischen LOLLIPOP und den anderen unterschieden?
Android-Entwickler
Ja, es ist möglich und es ist besser. Ich werde die Antwort korrigieren. und differenzieren müssen nicht auch
John
1
Dies ist die beste Antwort. Ich liebe dich buchstäblich.
Himanshu Rawat
1
Das ist die wahre Antwort.
Nyconing
1
Der Listener wird nie ausgeführt, wenn ich dies von onCreate () aus aufrufe. Vermisse ich etwas
Howettl
12

ich hoffe das hilft dir

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
quelle
5

Dies ist mein Code, um paddingRight und paddingBottom zu einer Ansicht hinzuzufügen, um der Navigationsleiste auszuweichen. Ich habe einige der Antworten hier kombiniert und zusammen mit isInMultiWindowMode eine spezielle Klausel für die Querformatorientierung erstellt. Der Schlüssel ist, navigation_bar_height zu lesen , aber auch zu überprüfen config_showNavigationBar zu , um sicherzustellen, dass wir die Höhe tatsächlich verwenden sollten.

Keine der vorherigen Lösungen hat bei mir funktioniert. Ab Android 7.0 müssen Sie den Multi Window Mode berücksichtigen. Dies unterbricht die Implementierungen, bei denen display.realSize mit display.size verglichen wird, da realSize die Abmessungen des gesamten Bildschirms (beide geteilte Fenster) und die Größe nur die Abmessungen Ihres App-Fensters angibt . Wenn Sie die Auffüllung auf diesen Unterschied einstellen, bleibt Ihre gesamte Ansicht aufgefüllt.

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

Die Höhe der unteren Navigationsleiste beträgt 48 dp (sowohl im Hoch- als auch im Querformat) und 42 dp, wenn die Leiste vertikal platziert wird.

Aritra Roy
quelle
4
Die Höhe beträgt normalerweise 48dp. Aber einige Hersteller können das ändern, einige benutzerdefinierte ROMs können das ändern und so weiter. Es ist besser, sich auf die Antwort von Danylo ( stackoverflow.com/a/26118045/1377145 ) zu verlassen, um die genaue Größe zu erhalten
Hugo Gresse
Das ist sogar noch besser. Vielen Dank.
Aritra Roy
vertikal platziert? wie im Querformat?
Sudocoder
1

Die von Egidijus vorgeschlagene Lösung funktioniert perfekt für Build.VERSION.SDK_INT> = 17

Bei der Ausführung der folgenden Anweisung mit Build.VERSION.SDK_INT <17 auf meinem Gerät wurde jedoch "NoSuchMethodException" angezeigt:

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

Ich habe die Methode getRealScreenSize () für folgende Fälle geändert:

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

Ich habe dieses Problem für alle Geräte behoben (einschließlich Nexus 5, Samsung Galaxy Nexus 6 Edge +, Samsung S10, Samsung Note II usw.). Ich denke, dies wird Ihnen helfen, geräteabhängige Probleme zu lösen.

Hier füge ich zwei Arten von Codes hinzu:

Java-Code (für natives 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;
    }
}

Und C # -Code (für 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
quelle
Display.getRealMetrics()erfordert API Level 17
Weizhi
if (Build.VERSION.SdkInt> = BuildVersionCodes.JellyBeanMr2) wurde bereits überprüft.
Sino Raj
Der Code muss behoben werden. Mein LG Nexus 5X erhält 0.0 aufgrund von: [1] Wenn screenOrientationStandard ist, hasPermanentMenuKeyist falsenur, wenn nicht drehen. [2] Wenn screenOrientationLandschaft ist, fällt sie in displayMetrics.heightPixels != realDisplayMetrics.heightPixels)die andere. [3] Wann screenOrientationist Porträt, hasPermanentMenuKeyist false.
Obst
es funktioniert, aber auf 4.x, wenn (hasPermanentMenuKey) Verschwendung ist, kommentieren Sie es
djdance
Ihre Bedingung sollte sein, wenn (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1). API 17 ist erforderlich, um getRealMetrics (DisplayMetrics outMetrics) öffentlich ungültig zu machen. Siehe Dokumente: developer.android.com/reference/kotlin/android/util/…
portfoliobuilder
1

Kombinieren Sie die Antwort von @egis und anderen - dies funktioniert gut auf einer Vielzahl von Geräten, die auf Pixel EMU, Samsung S6, Sony Z3 und Nexus 4 getestet wurden. Dieser Code verwendet die Anzeigeabmessungen, um die Verfügbarkeit der Navigationsleiste zu testen, und verwendet dann die tatsächliche Größe der Systemnavigationsleiste, falls vorhanden.

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

}

Gemeiner Mann
quelle
Wie würde ich diese Klasse verwenden, wenn ich die Höhe einer Textansicht programmgesteuert am unteren Rand der verwendbaren Höhe des Bildschirms festlegen möchte?
Naveed Abbas
1

So ermitteln Sie die Höhe der Navigationsleiste und der Statusleiste. Dieser Code funktioniert für mich auf einigen Huawei-Geräten und Samsung-Geräten . Die obige Lösung von Egis ist gut, auf einigen Geräten jedoch immer noch falsch. Also habe ich es verbessert.

Dies ist ein Code zum Abrufen der Höhe der Statusleiste

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
    }

Diese Methode gibt immer die Höhe der Navigationsleiste zurück, auch wenn die Navigationsleiste ausgeblendet ist.

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

HINWEIS: Bei Samsung A70 gibt diese Methode die Höhe der Statusleiste + die Höhe der Navigationsleiste zurück. Auf anderen Geräten (Huawei) wird nur die Höhe der Navigationsleiste und 0 zurückgegeben, wenn die Navigationsleiste ausgeblendet ist.

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

Dies ist ein Code zum Abrufen der Höhe der Navigationsleiste und der Statusleiste

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
quelle
1

Ich habe dies getan, es funktioniert auf jedem Gerät, das ich getestet habe, und sogar auf Emulatoren:

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

Hier ist, wie ich das gelöst habe. Ich habe eine versteckte untere Leiste erstellt, die aufgefüllt werden muss, je nachdem, ob eine Navigationsleiste vorhanden ist oder nicht (kapazitiv, auf dem Bildschirm oder nur vor dem Lutscher).


Aussicht

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
quelle
0

In meinem Fall, in dem ich so etwas haben wollte:

Bildschirmfoto

Ich musste das Gleiche tun wie von @Mdlc vorgeschlagen, aber wahrscheinlich etwas einfacher ( nur Targeting > = 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)

Es funktioniert auch in der Landschaft:

Landschaft

Bearbeiten Die obige Lösung funktioniert im Mehrfenstermodus nicht ordnungsgemäß, wenn das verwendbare Rechteck nicht nur aufgrund der Navigationsleiste, sondern auch aufgrund der benutzerdefinierten Fenstergröße kleiner ist. Eine Sache, die mir aufgefallen ist, ist, dass in mehreren Fenstern die Navigationsleiste nicht über der App schwebt, sodass wir auch ohne Änderungen an der DecorView-Auffüllung das richtige Verhalten haben:

Multi-Fenster ohne Änderungen an der Polsterung der Dekoransicht Einzelfenster ohne Änderungen an der Polsterung der Dekoransicht

Beachten Sie den Unterschied, wie sich die Navigationsleiste in diesen Szenarien über dem unteren Rand der App befindet. Glücklicherweise ist dies leicht zu beheben. Wir können überprüfen, ob die App mehrere Fenster ist. Der folgende Code enthält auch den Teil zum Berechnen und Anpassen der Position der Symbolleiste (vollständige Lösung: 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
    }

Ergebnis im Samsung-Popup-Modus:

Geben Sie hier die Bildbeschreibung ein

Bakhshi
quelle
0

Getesteter Code zum Abrufen der Höhe der Navigationsleiste (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;
}

Getesteter Code zum Abrufen der Höhe der Statusleiste (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;
}

Pixel in dp konvertieren:

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

Im Fall von Samsung S8 gab keine der oben genannten Methoden die richtige Höhe der Navigationsleiste an, daher habe ich den KeyboardHeightProvider- Tastaturhöhenanbieter Android verwendet . Und es gab mir Höhe in negativen Werten und für meine Layoutpositionierung habe ich diesen Wert in Berechnungen angepasst.

Hier ist 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"/>

Verwendung 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()
    }
}

Für weitere Hilfe können Sie einen Blick auf erweiterte Nutzung dieses haben hier .

Nilesh Deokar
quelle
0

Einfache einzeilige Lösung

Wie zum Beispiel in vielen der obigen Antworten vorgeschlagen

Es reicht möglicherweise nicht aus, nur die Höhe der Navigationsleiste zu ermitteln. Wir müssen prüfen, ob 1. die Navigationsleiste vorhanden ist, 2. sie sich unten oder rechts oder links befindet, 3. die App im Mehrfenstermodus geöffnet ist.

Glücklicherweise können Sie die lange Codierung leicht umgehen, indem Sie einfach android:fitsSystemWindows="true"Ihr Root-Layout festlegen. Das Android-System sorgt automatisch dafür, dass dem Stammlayout die erforderlichen Auffüllungen hinzugefügt werden, um sicherzustellen, dass die untergeordneten Ansichten nicht in die Navigationsleiste oder die Statusleistenbereiche gelangen.

Es gibt eine einfache einzeilige Lösung

android:fitsSystemWindows="true"

oder programmatisch

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

Sie können auch Root-Ansicht von erhalten

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

Weitere Informationen zum Abrufen der Root-Ansicht finden Sie unter https://stackoverflow.com/a/4488149/9640177

mayank1513
quelle