Bagaimana cara mendapatkan tinggi dan lebar Bilah Navigasi Android secara terprogram?

122

Bilah navigasi hitam di bagian bawah layar tidak mudah dilepas di Android. Ini telah menjadi bagian dari Android sejak 3.0 sebagai pengganti tombol perangkat keras. Ini gambarnya:

Bilah Sistem

Bagaimana saya bisa mendapatkan ukuran lebar dan tinggi elemen UI ini dalam piksel?

Kevik
sumber
Solusi terbaik untuk masalah ini ditambahkan di sini. Kita dapat mengidentifikasi, Apakah bilah navigasi ada di perangkat dengan membandingkan metrik tampilan dan metrik nyata. Lihat jawaban saya, saya menambahkan kode lengkap untuk mengetahui ukuran bilah navigasi sebenarnya untuk seluruh perangkat android.
Sino Raj

Jawaban:

178

Coba kode di bawah ini:

Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
    return resources.getDimensionPixelSize(resourceId);
}
return 0;
Sanket
sumber
7
Terima kasih. +1. Tahukah Anda seberapa stabil pendekatan ini? Apakah pengenal sumber daya dapat berubah di berbagai versi platform?
Ben Pearson
59
Perhatikan bahwa potongan kode ini tidak menghasilkan 0 pada perangkat tanpa bilah navigasi. Diuji pada Samsung S2 dan S3. Saya mendapat 72 dan 96.
Egis
4
@Egidijus Lihat jawaban saya, ini akan mengembalikan 0 untuk perangkat yang memiliki navigasi fisik stackoverflow.com/a/29938139/1683141
Mdlc
1
galaxy s6 edge juga tidak mengembalikan 0, meskipun tidak ada bilah navigasi di bagian bawah. mengembalikan 192.
agamov
5
Kode ini menunjukkan ukuran default bilah navigasi (coba juga navigation_bar_height_landscapeuntuk tinggi bilah navigasi dalam mode lanskap, dan navigation_bar_widthuntuk lebar bilah navigasi vertikal). Anda harus mencari tahu secara terpisah jika dan di mana bilah navigasi benar-benar ditampilkan, misalnya dengan menguji keberadaan tombol menu fisik. Mungkin Anda dapat menemukan cara lain dalam kode sumber Android di android.googlesource.com/platform/frameworks/base/+/…
user149408
103

Saya mendapatkan ukuran bilah navigasi dengan membandingkan ukuran layar yang dapat digunakan aplikasi dengan ukuran layar sebenarnya. Saya berasumsi bahwa bilah navigasi ada saat ukuran layar yang dapat digunakan aplikasi lebih kecil dari ukuran layar sebenarnya. Kemudian saya menghitung ukuran bilah navigasi. Metode ini berfungsi dengan API 14 dan yang lebih baru.

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

MEMPERBARUI

Untuk solusi yang memperhitungkan guntingan tampilan, silakan periksa jawaban John .

Egis
sumber
2
Ini adalah satu-satunya solusi yang berfungsi untuk saya di semua perbedaan perangkat yang telah saya uji. Permintaan saya adalah untuk menentukan apakah navBar berada di bagian bawah layar dengan mempertimbangkan orientasi akun. Saya telah sedikit mengubah kode dalam jawaban untuk mengembalikan saya ukuran bilah navigasi di bagian bawah layar, 0 menunjukkan itu tidak ada. Pengujian saya menunjukkan bahwa Nexus5 = potret: 144, Lanskap: 0 | Nexus7 = potret: 96, Lanskap: 96 | Samsung Note II = potret: 0, Lanskap: 0 | Samsung S10 = potret: 0, Lanskap: 0
se22
2
Saya juga dapat mengonfirmasi bahwa solusi Danylo hanya berfungsi pada beberapa model. Solusi dari Egidijus ini adalah solusi yang berfungsi lebih baik, dan telah berhasil untuk semua model yang diuji sejauh ini. Terima kasih untuk ini.
Bisclavret
2
Status bar adalah bagian dari layar yang dapat digunakan aplikasi.
Egis
1
Ini berfungsi, tetapi tidak mempertimbangkan apakah bilah yang ada tersembunyi atau tidak. Ada ide bagaimana cara memeriksa ini?
X-HuMan
1
Ini tidak berfungsi untuk Android P saat Gerakan Navigasi aktif, bukan bilah navigasi. Bisakah Anda membimbing saya bagaimana melakukan penyelesaian untuk situasi ini
Nik
36

Tinggi NavigationBar bervariasi untuk beberapa perangkat, tetapi juga untuk beberapa orientasi. Pertama Anda harus memeriksa apakah perangkat tersebut memiliki navbar, kemudian apakah perangkat tersebut adalah tablet atau bukan tablet (ponsel) dan terakhir Anda harus melihat orientasi perangkat untuk mendapatkan ketinggian yang benar.

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
sumber
Ini tidak bekerja untuk saya, Nexus5 mengembalikan nilai true untuk hasMenuKey dan hasBackKey oleh karena itu ia menganggap tidak ada navBar. (Nexus7 mengembalikan false dengan benar untuk hasMenuKey dan hasBackKey)
se22as
5
Saya telah menguji kode ini pada Nexus 5, dan hasilnya salah untuk keduanya. Apakah Anda yakin Anda tidak menggunakan emulator atau rom?
Mdlc
Ya saya menggunakan emulator, maaf saya harus mengklarifikasi dalam pernyataan saya sebelumnya
se22as
bukankah seharusnya begitu! hasMenuKey || ! hasBackKey bukan! hasMenuKey &&! hasBackKey? harap verifikasi
yongsunCN
2
Sayangnya, tidak berfungsi pada Sony Xperia Z3 hasBackKey = false!meskipun seharusnya benar. Yang berikut ini berhasil: boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
Hamzeh Soboh
27

Sebenarnya bilah navigasi di tablet (setidaknya Nexus 7) memiliki ukuran potret dan lanskap yang berbeda sehingga fungsi ini akan terlihat seperti ini:

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
sumber
6
Untuk mengambil orientasi dari konteks, gunakancontext.getResources().getConfiguration().orientation
Hugo Gresse
Solusi terbaik untuk masalah ini ditambahkan di sini. Kita dapat mengidentifikasi, Apakah bilah navigasi ada di perangkat dengan membandingkan metrik tampilan dan metrik nyata. Lihat jawaban saya, saya menambahkan kode lengkap untuk mengetahui ukuran bilah navigasi sebenarnya untuk seluruh perangkat android.
Sino Raj
17

Saya pikir jawaban yang lebih baik ada di sini karena memungkinkan Anda untuk mendapatkan ketinggian potongan yang rata juga.

Ambil tampilan root Anda, dan tambahkan setOnApplyWindowInsetsListener (atau Anda dapat mengganti onApplyWindowInsets darinya), dan mengambil insets.getSystemWindowInsets darinya.

Dalam aktivitas kamera saya, saya menambahkan padding yang sama dengan systemWindowInsetBottom ke tata letak bawah saya. Dan akhirnya, memperbaiki masalah guntingan.

Insets aktivitas kamera

dengan appcompat seperti ini

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

tanpa appcompat, ini:

mCameraSourcePreview.setOnApplyWindowInsetsListener((v, insets) -> { ... })
John
sumber
Apakah mungkin untuk digunakan setOnApplyWindowInsetsListenersebagai gantinya? Jika ya, bagaimana caranya? dan mengapa Anda membedakan antara LOLLIPOP dan yang lainnya?
Pengembang android
ya, itu mungkin, dan itu lebih baik. saya akan memperbaiki jawaban. dan membedakan tidak perlu terlalu
Yohanes
1
Inilah jawaban terbaik. Aku benar-benar mencintaimu.
Himanshu Rawat
1
Inilah jawaban sebenarnya.
nyconing
1
Listener tidak pernah dijalankan saat saya memanggil ini dari onCreate (). Apakah saya melewatkan sesuatu?
howettl
12

Saya harap ini membantu Anda

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

Ini adalah kode saya untuk menambahkan paddingRight dan paddingBottom ke View untuk menghindari Navigation Bar. Saya menggabungkan beberapa jawaban di sini dan membuat klausa khusus untuk orientasi lanskap bersama dengan isInMultiWindowMode. Kuncinya adalah membaca navigation_bar_height , tetapi juga memeriksa config_showNavigationBar untuk memastikan kita benar-benar harus menggunakan ketinggian.

Tidak ada solusi sebelumnya yang berhasil untuk saya. Pada Android 7.0 Anda harus mempertimbangkan Multi Window Mode. Ini memecah implementasi yang membandingkan display.realSize dengan display.size karena realSize memberi Anda dimensi seluruh layar (baik jendela terpisah) dan ukuran hanya memberi Anda dimensi dari jendela Aplikasi Anda. Menyetel padding ke perbedaan ini akan membuat seluruh tampilan Anda menjadi padding.

/** Adds padding to a view to dodge the navigation bar.

    Unfortunately something like this needs to be done since there
    are no attr or dimens value available to get the navigation bar
    height (as of December 2016). */
public static void addNavigationBarPadding(Activity context, View v) {
    Resources resources = context.getResources();
    if (hasNavigationBar(resources)) {
        int orientation = resources.getConfiguration().orientation;
        int size = getNavigationBarSize(resources);
        switch (orientation) {
        case Configuration.ORIENTATION_LANDSCAPE:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
                context.isInMultiWindowMode()) { break; }
            v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
                         v.getPaddingRight() + size, v.getPaddingBottom());
            break;
        case Configuration.ORIENTATION_PORTRAIT:
            v.setPadding(v.getPaddingLeft(), v.getPaddingTop(),
                         v.getPaddingRight(), v.getPaddingBottom() + size);
            break;
        }
    }
}

private static int getNavigationBarSize(Resources resources) {
    int resourceId = resources.getIdentifier("navigation_bar_height",
                                             "dimen", "android");
    return resourceId > 0 ? resources.getDimensionPixelSize(resourceId) : 0;
}

private static boolean hasNavigationBar(Resources resources) {
    int hasNavBarId = resources.getIdentifier("config_showNavigationBar",
                                              "bool", "android");
    return hasNavBarId > 0 && resources.getBoolean(hasNavBarId);
}
marcus
sumber
1

Tinggi bilah Navigasi bawah adalah 48 dp (dalam mode potret dan lanskap) dan 42 dp saat bilah ditempatkan secara vertikal.

Aritra Roy
sumber
4
Tingginya NORMAL 48dp. Tetapi beberapa pabrikan dapat mengubahnya, beberapa ROM khusus dapat mengubahnya, dan seterusnya. Lebih baik mengandalkan jawaban Danylo ( stackoverflow.com/a/26118045/1377145 ) untuk mendapatkan ukuran yang tepat
Hugo Gresse
Itu lebih baik. Terima kasih.
Aritra Roy
ditempatkan secara vertikal? seperti saat dalam mode lanskap?
sudocoder
1

Solusi yang diusulkan oleh Egidijus dan bekerja dengan sempurna untuk Build.VERSION.SDK_INT> = 17

Tetapi saya mendapatkan "NoSuchMethodException" selama eksekusi pernyataan berikut dengan Build.VERSION.SDK_INT <17 di perangkat saya:

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

Saya telah mengubah metode getRealScreenSize () untuk kasus seperti ini:

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

Saya menyelesaikan masalah ini untuk semua perangkat (termasuk Nexus 5, Samsung Galaxy Nexus 6 edge +, Samsung S10, Samsung Note II dll.). Saya pikir ini akan membantu Anda menangani masalah yang bergantung pada perangkat.

Di sini saya menambahkan dua jenis kode,

Kode Java (untuk Android Asli):

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

Dan kode C # (untuk 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
sumber
Display.getRealMetrics()membutuhkan API level 17
Weizhi
jika (Build.VERSION.SdkInt> = BuildVersionCodes.JellyBeanMr2) sudah dicentang.
Sino Raj
Kode perlu diperbaiki. Saya LG Nexus 5X akan mendapatkan 0.0 karena: [1] Ketika screenOrientationadalah default, hasPermanentMenuKeyadalah falsehanya jika tidak rotate. [2] Saat screenOrientationlanskap, ia jatuh ke dalam lanskap displayMetrics.heightPixels != realDisplayMetrics.heightPixels)lain. [3] Saat screenOrientationsedang potret, hasPermanentMenuKeyadalah false.
Buah
itu berfungsi, tetapi pada 4.x jika (hasPermanentMenuKey) adalah limbah, beri komentar
djdance
Kondisi Anda harus jika (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1). API 17 diperlukan untuk melakukan public void getRealMetrics (DisplayMetrics outMetrics). Lihat dokumen: developer.android.com/reference/kotlin/android/util/…
portfoliobuilder
1

Menggabungkan jawaban dari @egis dan lainnya - ini berfungsi dengan baik pada berbagai perangkat, diuji pada Pixel EMU, Samsung S6, Sony Z3, Nexus 4. Kode ini menggunakan dimensi tampilan untuk menguji ketersediaan bilah navigasi dan kemudian menggunakan yang sebenarnya ukuran bilah navigasi sistem jika ada.

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

}

Jahat
sumber
Jika saya ingin menyetel ketinggian TextView secara terprogram ke bagian bawah ketinggian layar yang dapat digunakan, bagaimana saya akan menggunakan kelas ini?
Naveed Abbas
1

Cara mendapatkan ketinggian bilah navigasi dan bilah status. Kode ini berfungsi untuk saya di beberapa perangkat Huawei dan perangkat Samsung . Solusi Egis di atas bagus, namun masih salah di beberapa perangkat. Jadi, saya memperbaikinya.

Ini adalah kode untuk mendapatkan ketinggian bilah status

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
    }

Metode ini selalu mengembalikan ketinggian bilah navigasi bahkan saat bilah navigasi disembunyikan.

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

CATATAN: pada Samsung A70, metode ini mengembalikan tinggi bilah status + tinggi bilah navigasi. Di perangkat lain (Huawei), ini hanya mengembalikan tinggi bilah Navigasi dan mengembalikan 0 ketika bilah navigasi disembunyikan.

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

Ini adalah kode untuk mendapatkan ketinggian bilah navigasi dan bilah status

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

Saya telah melakukan ini, ini berfungsi pada setiap perangkat yang saya uji, dan bahkan pada emulator:

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

Inilah cara saya menyelesaikan ini. Saya membuat bilah bawah yang dapat disembunyikan yang membutuhkan bantalan tergantung pada apakah ada bilah navigasi atau tidak (kapasitif, di layar atau hanya pra lollipop).


Melihat

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

Dalam kasus saya di mana saya ingin memiliki sesuatu seperti ini:

Screenshot

Saya harus mengikuti hal yang sama seperti yang disarankan oleh @Mdlc tetapi mungkin sedikit lebih sederhana ( hanya menargetkan > = 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)

Ini berfungsi pada lanskap juga:

pemandangan

Edit Solusi di atas tidak berfungsi dengan benar dalam mode multi-jendela di mana persegi panjang yang dapat digunakan tidak lebih kecil hanya karena bilah navigasi tetapi juga karena ukuran jendela khusus. Satu hal yang saya perhatikan adalah bahwa di multi-jendela bilah navigasi tidak melayang di atas aplikasi sehingga bahkan tanpa perubahan pada padding DecorView kami memiliki perilaku yang benar:

Multi-jendela tanpa perubahan pada bantalan tampilan dekorasi Jendela tunggal tanpa perubahan pada bantalan tampilan dekorasi

Perhatikan perbedaan antara bagaimana bilah navigasi mengarahkan kursor ke bawah aplikasi dalam skenario ini. Untungnya, ini mudah diperbaiki. Kami dapat memeriksa apakah aplikasi multi jendela. Kode di bawah ini juga menyertakan bagian untuk menghitung dan mengatur posisi toolbar (solusi lengkap: 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
    }

Hasil pada mode popup Samsung:

masukkan deskripsi gambar di sini

Bakhshi
sumber
0

Kode yang diuji untuk mendapatkan ketinggian bilah navigasi (dalam piksel):

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

Kode yang diuji untuk mendapatkan tinggi bilah status (dalam piksel):

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

Mengonversi piksel menjadi dp:

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

Dalam kasus Samsung S8, tidak ada metode yang disediakan di atas yang memberikan ketinggian bilah navigasi yang tepat jadi saya menggunakan penyedia ketinggian keyboard KeyboardHeightProvider android . Dan itu memberi saya ketinggian dalam nilai negatif dan untuk posisi tata letak saya, saya menyesuaikan nilai itu dalam perhitungan.

Ini dia 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"/>

Penggunaan dalam 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()
    }
}

Untuk bantuan lebih lanjut, Anda dapat melihat penggunaan lanjutan ini di sini .

Nilesh Deokar
sumber
0

Solusi Satu Baris Sederhana

Seperti yang disarankan dalam banyak jawaban di atas, misalnya

Hanya mendapatkan tinggi bilah navigasi mungkin tidak cukup. Kita perlu mempertimbangkan apakah 1. bilah navigasi ada, 2. apakah di bagian bawah, atau kanan atau kiri, 3. apakah aplikasi terbuka dalam mode multi-jendela.

Untungnya Anda dapat dengan mudah melewati semua pengkodean panjang hanya dengan mengatur android:fitsSystemWindows="true"tata letak root Anda. Sistem Android akan secara otomatis menangani penambahan pengisi yang diperlukan ke tata letak root untuk memastikan bahwa tampilan anak tidak masuk ke bilah navigasi atau wilayah bilah status.

Ada solusi satu baris sederhana

android:fitsSystemWindows="true"

atau secara terprogram

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

Anda juga bisa mendapatkan tampilan root

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

Untuk detail lebih lanjut tentang mendapatkan tampilan root, lihat - https://stackoverflow.com/a/4488149/9640177

mayank1513
sumber