Android Gezinme Çubuğunun yüksekliğini ve genişliğini programlı olarak nasıl edinirim?


122

Ekranın altındaki siyah gezinme çubuğu Android'de kolayca çıkarılamaz. Donanım düğmelerinin yerini almak üzere 3.0'dan beri Android'in bir parçası. Burada bir resim var:

Sistem Çubuğu

Bu UI öğesinin genişliğini ve yüksekliğini piksel cinsinden nasıl alabilirim?


Bu problem için en iyi çözüm buraya eklenmiştir. Ekran metriklerini ve gerçek ölçümleri karşılaştırarak cihazda gezinme çubuğunun mevcut olup olmadığını belirleyebiliriz. Cevabıma bakın, tüm android cihazlar için gerçek gezinme çubuğu boyutunu bulmak için tam kod ekledim.
Sino Raj

Yanıtlar:


178

Aşağıdaki kodu deneyin:

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

7
Teşekkürler. +1. Bu yaklaşımın ne kadar kararlı olduğunu biliyor musunuz? Kaynak tanımlayıcıları farklı platform sürümlerinde değişime tabi olacak mı?
Ben Pearson

59
Bu kod parçasının gezinme çubuğu olmayan cihazlarda 0 döndürmediğini unutmayın. Samsung S2 ve S3 üzerinde test edilmiştir. 72 ve 96 aldım.
Egis

4
@Egidijus Cevabıma bakın, fiziksel gezinme stackoverflow
Com /a/29938139/1683141

1
galaxy s6 edge, altta gezinme çubuğu olmamasına rağmen, 0 değerini de döndürmüyor. 192
değerini

5
Bu kod, gezinme çubuğunun varsayılan boyutunu gösterir ( navigation_bar_height_landscapeyatay modda gezinme çubuğu yüksekliği ve navigation_bar_widthdikey gezinme çubuğunun genişliği için de deneyin ). Gezinme çubuğunun gerçekte nerede göründüğünü ayrı ayrı bulmanız gerekir, örneğin fiziksel bir menü düğmesinin varlığını test ederek. Belki android.googlesource.com/platform/frameworks/base/+/…
user149408

103

Uygulamada kullanılabilen ekran boyutunu gerçek ekran boyutuyla karşılaştırarak gezinme çubuğu boyutunu elde ediyorum. Uygulama tarafından kullanılabilen ekran boyutu gerçek ekran boyutundan daha küçük olduğunda gezinme çubuğunun mevcut olduğunu varsayıyorum. Sonra gezinti çubuğu boyutunu hesaplıyorum. Bu yöntem, API 14 ve üzeri ile çalışır.

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

GÜNCELLEME

Ekran kesintilerini hesaba katan bir çözüm için lütfen John'un cevabını kontrol edin .


2
Test ettiğim tüm cihazlarda benim için çalışan tek çözüm budur. Benim isteğim, yönlendirmeyi dikkate alarak navBar'ın ekranın altında olup olmadığını belirlemektir. Ekranın altındaki gezinme çubuğunun boyutunu döndürmek için yanıttaki kodu biraz değiştirdim, 0 mevcut olmadığını gösterir. Testim Nexus5 = portre: 144, Yatay: 0 | Nexus7 = dikey: 96, Yatay: 96 | Samsung Note II = portre: 0, Yatay: 0 | Samsung S10 = portre: 0, Manzara: 0
se22as

2
Danylo'nun çözümünün sadece bazı modellerde işe yaradığını da teyit edebilirim. Egidijus'un bu çözümü daha iyi çalışan bir çözümdür ve şimdiye kadar test edilen tüm modeller için işe yaradı. Bunun için teşekkür ederim.
Bisclavret

2
Durum çubuğu, uygulamanın kullanılabilir ekranının bir parçasıdır.
Egis

1
Bu çalışır, ancak mevcut çubuğun gizli olup olmadığını dikkate almaz. Bunu nasıl kontrol edeceğine dair bir fikrin var mı?
X-HuMan

1
Gezinme çubuğu yerine Gezinme Hareketi açıkken Android P için çalışmıyor. Lütfen bana bu durumun nasıl çözüleceğine dair rehberlik eder misiniz
Nik

36

NavigationBar yüksekliği bazı cihazlar için, ancak bazı yönler için de değişir. Öncelikle cihazın bir navbar olup olmadığını kontrol etmelisiniz, ardından cihaz bir tablet mi yoksa tablet değil mi (telefon) ve son olarak doğru yüksekliği elde etmek için cihazın yönüne bakmalısınız.

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

Bu benim için çalışmıyor, Nexus5 hasMenuKey ve hasBackKey için true döndürüyor, bu nedenle navBar olmadığını düşünüyor. (Nexus7 hasMenuKey ve hasBackKey için doğru şekilde false değerini döndürür)
se22as

5
Bu kodu Nexus 5'te test ettim ve her ikisi için de yanlış döndürüyor. Bir emülatörde veya romda olmadığına emin misin?
Mdlc

Evet bir emülatör kullanıyordum, üzgünüm daha önce açıklamamda açıklığa kavuşturmalıydım
se22as

bu olmamalı! hasMenuKey || ! hasMenuKey yerine! hasBackKey &&! hasBackKey? lütfen doğrulayın
yongsunCN

2
Ne yazık ki, hasBackKey = false!doğru olması gerekse de Sony Xperia Z3 üzerinde çalışmıyor . Bunun yerine aşağıdakiler işe yaradı: boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
Hamzeh Soboh

27

Aslında tabletlerdeki gezinme çubuğunun (en azından Nexus 7) dikey ve yatay olarak boyutları farklı olduğundan bu işlev şu şekilde görünmelidir:

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
Yönü bağlamdan almak için kullanıncontext.getResources().getConfiguration().orientation
Hugo Gresse

Bu problem için en iyi çözüm buraya eklenmiştir. Ekran metriklerini ve gerçek ölçümleri karşılaştırarak cihazda gezinme çubuğunun mevcut olup olmadığını belirleyebiliriz. Cevabıma bakın, tüm android cihazlar için gerçek gezinme çubuğu boyutunu bulmak için tam kod ekledim.
Sino Raj

17

Bence daha iyi cevap burada çünkü kesme yüksekliğini bile almanıza izin veriyor.

Kök görünümünüzü alın ve setOnApplyWindowInsetsListener ekleyin (veya onApplyWindowInsets'i buradan geçersiz kılabilirsiniz) ve ondan insets.getSystemWindowInsets alın.

Kamera etkinliğimde, alt düzenime systemWindowInsetBottom'a eşit dolgu ekliyorum. Ve son olarak, kesme sorununu düzeltir.

Kamera etkinliği ekleri

appcompat ile bu böyle

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

appcompat olmadan, bu:

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

setOnApplyWindowInsetsListenerBunun yerine kullanmak mümkün mü ? Öyleyse nasıl? ve neden LOLLIPOP ile diğerleri arasında ayrım yaptınız?
android geliştiricisi

evet, mümkün ve daha iyi. cevabı düzelteceğim. ve farklılaşmaya da gerek yok
Yuhanna

1
Bu en iyi cevap. Seni gerçekten seviyorum
Himanshu Rawat

1
Bu gerçek cevap.
nyconing

1
Bunu onCreate () 'den çağırdığımda dinleyici asla çalıştırılmıyor. Bir şey mi kaçırıyorum?
howettl

12

umarım bu sana yardımcı olur

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

Bu, Gezinme Çubuğunu atlatmak için bir Görünüme paddingRight ve paddingBottom eklemek için kodumdur. Burada bazı cevapları birleştirdim ve isInMultiWindowMode ile birlikte yatay yönlendirme için özel bir madde yaptım. Anahtar, navigation_bar_height'ı okumak , aynı zamanda yüksekliği gerçekten kullanmamız gerektiğinden emin olmak için config_showNavigationBar'ı kontrol etmektir .

Önceki çözümlerin hiçbiri benim için işe yaramadı. Android 7.0'dan itibaren Çoklu Pencere Modunu göz önünde bulundurmanız gerekiyor. Bu, display.realSize ile display.size'yi karşılaştıran uygulamaları bozar , çünkü realSize size tüm ekranın boyutlarını (hem bölünmüş pencereler) hem de size yalnızca Uygulama pencerenizin boyutlarını verir. Bu farklılığa dolgu ayarlamak, tüm görünümünüzün dolgulu kalmasını sağlayacaktır.

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

Alt Gezinme çubuğunun yüksekliği 48dp'dir (hem dikey hem de yatay modda) ve çubuk dikey olarak yerleştirildiğinde 42dp'dir.


4
Yükseklik NORMALDE 48dp'dir. Ancak bazı üreticiler bunu değiştirebilir, bazı özel ROM'lar bunu değiştirebilir ve benzeri. Tam boyuta sahip olmak için Danylo cevabına ( stackoverflow.com/a/26118045/1377145 ) güvenmek daha iyidir
Hugo Gresse

Bu daha da iyi. Teşekkürler.
Aritra Roy

dikey olarak yerleştirilmiş mi? manzara modunda olduğu gibi?
sudocoder

1

Egidijus tarafından önerilen ve Build.VERSION.SDK_INT> = 17 için mükemmel çalışan çözüm

Ancak aygıtımda Build.VERSION.SDK_INT <17 ile aşağıdaki ifadenin yürütülmesi sırasında "NoSuchMethodException" aldım:

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

Bu tür durumlar için getRealScreenSize () yöntemini değiştirdim:

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

1

Bu sorunu tüm cihazlar için çözdüm (Nexus 5, Samsung Galaxy Nexus 6 edge +, Samsung S10, Samsung Note II vb. Dahil). Bunun cihaza bağlı sorunları çözmenize yardımcı olacağını düşünüyorum.

Burada iki tür kod ekliyorum,

Java Kodu (Yerel Android için):

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

Ve C # kodu (Xamarin Forms / Android için)

int resourceId = -1;
        IWindowManager windowManager = null;
        Display defaultDisplay = null;
        DisplayMetrics displayMatrics = null;
        DisplayMetrics realMatrics = null;
        Resources resources = null;

        public double NavigationBarHeight
        {
            get
            {
                try
                {
                    windowManager = Forms.Context.GetSystemService(Context.WindowService).JavaCast<IWindowManager>();
                    defaultDisplay = windowManager.DefaultDisplay;
                    displayMatrics = new DisplayMetrics();
                    if (Build.VERSION.SdkInt >= BuildVersionCodes.JellyBeanMr2)
                    {
                        realMatrics = new DisplayMetrics();
                        defaultDisplay.GetMetrics(displayMatrics);
                        defaultDisplay.GetRealMetrics(realMatrics);
                        if (displayMatrics.HeightPixels != realMatrics.HeightPixels)
                        {
                            resources = Forms.Context.Resources;
                            return GetHeightOfNivigationBar();
                        }
                    }
                    else {
                        resources = Forms.Context.Resources;
                        resourceId = resources.GetIdentifier("config_showNavigationBar", "bool", "android");
                        if (resourceId > 0 && resources.GetBoolean(resourceId))
                            return GetHeightOfNivigationBar();
                    }
                }
                catch (Exception e) { }
                return 0;
            }
        }

        private double GetHeightOfNivigationBar()
        {
            resourceId = resources.GetIdentifier("navigation_bar_height", "dimen", "android");
            if (!ViewConfiguration.Get(Forms.Context).HasPermanentMenuKey && resourceId > 0)
            {
                return resources.GetDimensionPixelSize(resourceId) / displayMatrics.Density;
            }
            return 0;
        }

Display.getRealMetrics()API seviye 17 gerektirir
Weizhi

(Build.VERSION.SdkInt> = BuildVersionCodes.JellyBeanMr2) zaten işaretlenmişse.
Sino Raj

Kodun düzeltilmesi gerekiyor. LG Nexus 5X'im şu nedenlerle 0.0 alacak: [1] screenOrientationVarsayılan olduğunda hasPermanentMenuKey, falseyalnızca döndürülmüyorsa geçerlidir. [2] screenOrientationManzara olduğunda , displayMetrics.heightPixels != realDisplayMetrics.heightPixels)başka bir yere düşer . [3] screenOrientationPortre ne zaman , hasPermanentMenuKeyolur false.
Fruit

çalışıyor, ancak 4.x'te (hasPermanentMenuKey) israfsa, yorum yapın
djdance

Koşulunuz (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1) olmalıdır. Genel void getRealMetrics (DisplayMetrics outMetrics) yapmak için API 17 gereklidir. Dokümanlara bakın: developer.android.com/reference/kotlin/android/util/…
portfoliobuilder

1

@Egis ve diğerlerinin yanıtı birleştirildiğinde - bu, Pixel EMU, Samsung S6, Sony Z3, Nexus 4 üzerinde test edilen çeşitli cihazlarda iyi çalışır. Bu kod, gezinme çubuğunun kullanılabilirliğini test etmek için ekran boyutlarını kullanır ve ardından gerçek varsa sistem gezinme çubuğu boyutu.

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

}


Bir TextView yüksekliğini programlı olarak ekranın kullanılabilir yüksekliğinin altına ayarlamak istersem, bu sınıfı nasıl kullanırım?
Naveed Abbas

1

Gezinme çubuğunun ve durum çubuğunun yüksekliği nasıl alınır. Bu kod benim için bazı Huawei cihazlarında ve Samsung cihazlarında çalışıyor . Egis'in yukarıdaki çözümü iyidir, ancak bazı cihazlarda hala yanlıştır. Ben de geliştirdim.

Bu, durum çubuğunun yüksekliğini almak için koddur

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
    }

Bu yöntem, gezinme çubuğu gizlendiğinde bile her zaman gezinme çubuğunun yüksekliğini döndürür.

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

NOT: Samsung A70'te bu yöntem, durum çubuğunun yüksekliğini + gezinme çubuğunun yüksekliğini döndürür. Diğer cihazlarda (Huawei), yalnızca Gezinme çubuğunun yüksekliğini döndürür ve gezinme çubuğu gizlendiğinde 0 değerini döndürür.

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

Bu, gezinme çubuğunun ve durum çubuğunun yüksekliğini gösteren koddur

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

Bunu yaptım, test ettiğim her cihazda ve hatta emülatörlerde çalışıyor:

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

İşte bunu nasıl çözdüm. Gezinme çubuğu olup olmadığına (kapasitif, ekran üstü veya sadece ön lolipop) bağlı olarak dolguya ihtiyaç duyan gizlenebilir bir alt çubuk yaptım.


Görünüm

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

Benim durumumda böyle bir şeye sahip olmak istedim:

Ekran görüntüsü

@Mdlc tarafından önerilen aynı şeyi takip etmek zorunda kaldım, ancak muhtemelen biraz daha basit ( yalnızca > = 21 hedefleme ):

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

Manzara üzerinde de çalışır:

peyzaj

Düzenleme Yukarıdaki çözüm, kullanılabilir dikdörtgenin yalnızca gezinme çubuğu nedeniyle değil, aynı zamanda özel pencere boyutu nedeniyle daha küçük olduğu çoklu pencere modunda doğru şekilde çalışmaz. Fark ettiğim bir şey, çoklu pencerede gezinme çubuğunun uygulamanın üzerine gelmemesidir, bu nedenle DecorView dolgusunda herhangi bir değişiklik olmasa bile doğru davranışa sahibiz:

Dekor görünümü dolgusunda değişiklik yapılmayan çoklu pencere Dekor görünümü dolgusunda değişiklik yapılmayan tek pencere

Senaryolarda gezinme çubuğunun uygulamanın alt kısmına nasıl geldiği arasındaki farka dikkat edin. Neyse ki, bunu düzeltmek kolaydır. Uygulamanın çoklu pencere olup olmadığını kontrol edebiliriz. Aşağıdaki kod, araç çubuğunun konumunu hesaplamak ve ayarlamak için bölüm de içerir (tam çözüm: https://stackoverflow.com/a/14213035/477790 )

    // kotlin
    // Let the window flow into where window decorations are
    window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN)
    window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)

    // calculate where the bottom of the page should end up, considering the navigation bar (back buttons, ...)
    val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
    val realSize = Point()
    windowManager.defaultDisplay.getRealSize(realSize);
    val usableRect = Rect()
    windowManager.defaultDisplay.getRectSize(usableRect)
    Toast.makeText(this, "Usable Screen: " + usableRect + " real:" + realSize, Toast.LENGTH_LONG).show()

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || !isInMultiWindowMode) {
        window.decorView.setPadding(usableRect.left, usableRect.top, realSize.x - usableRect.right, realSize.y - usableRect.bottom)
        // move toolbar/appbar further down to where it should be and not to overlap with status bar
        val layoutParams = ConstraintLayout.LayoutParams(appBarLayout.layoutParams as ConstraintLayout.LayoutParams)
        layoutParams.topMargin = getSystemSize(Constants.statusBarHeightKey)
        appBarLayout.layoutParams = layoutParams
    }

Samsung açılır modundaki sonuç:

görüntü açıklamasını buraya girin


0

Gezinme çubuğunun yüksekliğini almak için test edilen kod (piksel cinsinden):

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

Durum çubuğunun yüksekliğini almak için test edilen kod (piksel cinsinden):

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

Pikselleri dp'ye dönüştürme:

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

0

Samsung S8 durumunda, yukarıda verilen yöntemlerden hiçbiri gezinme çubuğunun uygun yüksekliğini vermiyordu, bu yüzden KeyboardHeightProvider klavye yükseklik sağlayıcısı androidini kullandım . Ve bana negatif değerlerde yükseklik verdi ve düzen konumlandırmam için bu değeri hesaplamalarda ayarladım.

İşte 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"/>

Kullanım 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()
    }
}

Başka yardım için bu gelişmiş kullanım bakmak olabilir burada .


0

Basit Tek Hatlı Çözüm

Örneğin, yukarıdaki yanıtların çoğunda önerildiği gibi

Sadece gezinme çubuğunun yüksekliğini almak yeterli olmayabilir. 1. gezinme çubuğunun var olup olmadığını, 2. altta mı yoksa sağda mı yoksa solda mı, 3. uygulamanın çoklu pencere modunda açık olup olmadığını değerlendirmemiz gerekir.

Neyse ki android:fitsSystemWindows="true", kök düzeninizi ayarlayarak tüm uzun kodlamayı kolayca atlayabilirsiniz . Android sistemi, alt görünümlerin gezinme çubuğuna veya durum çubuğu bölgelerine girmediğinden emin olmak için kök düzenine gerekli dolguyu otomatik olarak ekleyecektir.

Tek satırlık basit bir çözüm var

android:fitsSystemWindows="true"

veya programatik olarak

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

ayrıca kök görünümü elde edebilirsiniz

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

Kök görünümü alma hakkında daha fazla ayrıntı için - https://stackoverflow.com/a/4488149/9640177

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.