Làm cách nào để lấy chiều cao và chiều rộng của Thanh điều hướng Android theo lập trình?


122

Thanh điều hướng màu đen ở cuối màn hình không dễ tháo rời trong Android. Nó là một phần của Android kể từ phiên bản 3.0 để thay thế cho các nút phần cứng. Đây là một hình ảnh:

Thanh hệ thống

Làm cách nào để lấy kích thước chiều rộng và chiều cao của phần tử giao diện người dùng này tính bằng pixel?


Giải pháp tốt nhất cho vấn đề này được thêm vào đây. Chúng ta có thể xác định, Thanh điều hướng có trong thiết bị hay không bằng cách so sánh số liệu hiển thị và số liệu thực. Hãy xem câu trả lời của tôi, tôi đã thêm mã đầy đủ để tìm ra kích thước thanh điều hướng thực tế cho toàn bộ thiết bị Android.
Sino Raj

Câu trả lời:


178

Hãy thử mã dưới đây:

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

7
Cảm ơn. +1. Bạn có biết cách tiếp cận này ổn định như thế nào không? Các số nhận dạng tài nguyên có thể thay đổi trên các phiên bản nền tảng khác nhau không?
Ben Pearson

59
Lưu ý rằng đoạn mã này không trả về 0 trên các thiết bị không có thanh điều hướng. Đã thử nghiệm trên Samsung S2 và S3. Tôi nhận được 72 và 96.
Egis

4
@Egidijus Hãy xem câu trả lời của tôi, nó sẽ trả về 0 cho các thiết bị có điều hướng vật lý stackoverflow.com/a/29938139/1683141
Mdlc

1
galaxy s6 edge cũng không trả về 0, mặc dù không có thanh điều hướng ở phía dưới. trả về 192.
agamov

5
Mã này hiển thị kích thước mặc định của thanh điều hướng (cũng thử navigation_bar_height_landscapeđối với chiều cao của thanh điều hướng ở chế độ ngang và navigation_bar_widthchiều rộng của thanh điều hướng dọc). Bạn phải tìm hiểu riêng nếu và vị trí thanh điều hướng thực sự đang hiển thị, ví dụ: bằng cách kiểm tra sự hiện diện của nút menu vật lý. Có thể bạn có thể tìm thấy một số cách khác trong mã souce Android tại android.googlesource.com/platform/frameworks/base/+/…
user149408

103

Tôi nhận được kích thước thanh điều hướng bằng cách so sánh kích thước màn hình có thể sử dụng ứng dụng với kích thước màn hình thực. Tôi giả định rằng thanh điều hướng xuất hiện khi kích thước màn hình có thể sử dụng ứng dụng nhỏ hơn kích thước màn hình thực. Sau đó, tôi tính toán kích thước thanh điều hướng. Phương pháp này hoạt động với API 14 trở lên.

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

CẬP NHẬT

Đối với một giải pháp có tính đến phần cắt hiển thị, vui lòng kiểm tra câu trả lời của John .


2
Đây là giải pháp duy nhất phù hợp với tôi trên tất cả các thiết bị khác nhau mà tôi đã thử nghiệm. Yêu cầu của tôi là xác định xem navBar ở cuối màn hình có tính đến hướng hay không. Tôi đã chỉnh sửa một chút mã trong câu trả lời để chỉ trả lại cho tôi kích thước của thanh điều hướng ở cuối màn hình, 0 cho biết nó không có mặt. Thử nghiệm của tôi cho thấy Nexus5 = dọc: 144, Ngang: 0 | Nexus7 = dọc: 96, Ngang: 96 | Samsung Note II = dọc: 0, Ngang: 0 | Samsung S10 =
dọc

2
Tôi cũng có thể xác nhận giải pháp của Danylo chỉ hoạt động trên một số kiểu máy. Giải pháp này của Egidijus là một giải pháp hoạt động tốt hơn và đã hoạt động cho tất cả các mô hình được thử nghiệm cho đến nay. Cảm ơn vì điều này.
Bisclavret

2
Thanh trạng thái là một phần của màn hình có thể sử dụng ứng dụng.
Egis

1
Điều này hoạt động, nhưng không xem xét thanh hiện tại có bị ẩn hay không. Bất kỳ ý tưởng làm thế nào để kiểm tra điều này?
X-HuMan

1
Nó không hoạt động đối với Android P khi cử chỉ Điều hướng được bật thay vì thanh điều hướng. Bạn có thể vui lòng hướng dẫn tôi cách giải quyết tình huống này không
Nik

36

Chiều cao NavigationBar thay đổi đối với một số thiết bị, nhưng cũng có thể đối với một số hướng. Đầu tiên bạn phải kiểm tra xem thiết bị có thanh điều hướng hay không, sau đó là máy tính bảng hay không phải máy tính bảng (điện thoại) và cuối cùng bạn phải xem hướng của thiết bị để có được chiều cao chính xác.

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

Điều này không hiệu quả với tôi, Nexus5 trả về true cho hasMenuKey và hasBackKey, do đó nó cho rằng không có navBar. (Nexus7 trả về false một cách chính xác cho hasMenuKey và hasBackKey)
se22 vào

5
Tôi đã kiểm tra mã này trên Nexus 5 và nó trả về false cho cả hai. Bạn có chắc mình không sử dụng trình giả lập hoặc rom không?
Mdlc

Có, tôi đã sử dụng một mô phỏng, xin lỗi tôi nên đã làm rõ trong bản Tuyên Bố tôi trước đây
se22as

không nên thế này! hasMenuKey || ! hasBackKey thay vì! hasMenuKey &&! hasBackKey? vui lòng xác minh
yongsunCN

2
Thật không may, nó không hoạt động trên Sony Xperia Z3 hasBackKey = false!mặc dù nó phải là sự thật. Sau đây làm việc thay vì: boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
HAMZEH Soboh

27

Trên thực tế, thanh điều hướng trên máy tính bảng (ít nhất là Nexus 7) có kích thước khác nhau ở chế độ dọc và ngang, vì vậy chức năng này sẽ giống như sau:

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
Để lấy định hướng từ bối cảnh, sử dụngcontext.getResources().getConfiguration().orientation
Hugo Gresse

Giải pháp tốt nhất cho vấn đề này được thêm vào đây. Chúng ta có thể xác định, Thanh điều hướng có trong thiết bị hay không bằng cách so sánh số liệu hiển thị và số liệu thực. Hãy xem câu trả lời của tôi, tôi đã thêm mã đầy đủ để tìm ra kích thước thanh điều hướng thực tế cho toàn bộ thiết bị Android.
Sino Raj

17

Tôi nghĩ câu trả lời tốt hơn là ở đây bởi vì nó cho phép bạn có được chiều cao thậm chí là vượt trội.

Lấy chế độ xem gốc của bạn và thêm setOnApplyWindowInsetsListener (hoặc bạn có thể ghi đè onApplyWindowInsets từ nó) và lấy insets.getSystemWindowInsets từ nó.

Trong hoạt động máy ảnh của mình, tôi thêm phần đệm bằng systemWindowInsetBottom vào bố cục dưới cùng của mình. Và cuối cùng, nó khắc phục sự cố bị cắt.

Cài đặt hoạt động máy ảnh

với appcompat nó như thế này

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

không có appcompat, cái này:

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

Có thể sử dụng setOnApplyWindowInsetsListenerthay thế không? Nếu vậy, làm thế nào? và tại sao bạn lại phân biệt giữa LOLLIPOP và phần còn lại?
nhà phát triển android

vâng, nó có thể, và nó tốt hơn. tôi sẽ sửa câu trả lời. và phân biệt không cần quá
John

1
Đây là câu trả lời tốt nhất. Tôi thực sự yêu bạn.
Himanshu Rawat

1
Đây là câu trả lời thực sự.
nyconing

1
Trình nghe không bao giờ được thực thi khi tôi gọi điều này từ onCreate (). Tui bỏ lỡ điều gì vậy?
howettl

12

Tôi hy vọng cái này sẽ giúp bạn

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

Đây là mã của tôi để thêm paddingRight và paddingBottom vào Chế độ xem để né Thanh điều hướng. Tôi đã kết hợp một số câu trả lời ở đây và tạo một điều khoản đặc biệt cho hướng ngang cùng với isInMultiWindowMode. Điều quan trọng là đọc navigation_bar_height , nhưng cũng kiểm tra config_showNavigationBar để đảm bảo rằng chúng ta thực sự nên sử dụng chiều cao.

Không có giải pháp nào trước đây phù hợp với tôi. Kể từ Android 7.0, bạn phải xem xét Chế độ đa cửa sổ. Điều này phá vỡ các triển khai so sánh display.realSize với display.sizerealSize cung cấp cho bạn kích thước của toàn màn hình (cả hai cửa sổ chia nhỏ) và kích thước chỉ cung cấp cho bạn kích thước của cửa sổ Ứng dụng của bạn. Đặt padding thành sự khác biệt này sẽ khiến toàn bộ khung nhìn của bạn bị padding.

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

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

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

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

1

Chiều cao của thanh Điều hướng dưới cùng là 48dp (ở cả chế độ dọc và ngang) và là 42dp khi đặt thanh này theo chiều dọc.


4
Chiều cao BÌNH THƯỜNG là 48dp. Nhưng một số nhà sản xuất có thể thay đổi điều đó, một số ROM tùy chỉnh có thể thay đổi điều đó, v.v. Tốt hơn nên dựa vào câu trả lời của Danylo ( stackoverflow.com/a/26118045/1377145 ) để có kích thước chính xác
Hugo Gresse 12/02/15

Điều đó thậm chí còn tốt hơn. Cảm ơn.
Aritra Roy

đặt thẳng đứng? như khi nó ở chế độ ngang?
sudocoder

1

Giải pháp do Egidijus đề xuất và hoạt động hoàn hảo cho Build.VERSION.SDK_INT> = 17

Nhưng tôi nhận được "NoSuchMethodException" trong khi thực hiện câu lệnh sau với Build.VERSION.SDK_INT <17 trên thiết bị của mình:

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

Tôi đã sửa đổi phương thức getRealScreenSize () cho những trường hợp như vậy:

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

1

Tôi đã khắc phục sự cố này cho tất cả các thiết bị (bao gồm Nexus 5, Samsung Galaxy Nexus 6 edge +, Samsung S10, Samsung Note II, v.v.). Tôi nghĩ điều này sẽ giúp bạn xử lý các vấn đề phụ thuộc vào thiết bị.

Ở đây tôi đang thêm hai loại mã,

Mã Java (dành cho Android gốc):

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

Và mã C # (dành cho Xamarin Forms / Android)

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

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

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

Display.getRealMetrics()yêu cầu API cấp 17
Weizhi

nếu (Build.VERSION.SdkInt> = BuildVersionCodes.JellyBeanMr2) đã được chọn.
Sino Raj

Mã cần sửa. LG Nexus 5X của tôi sẽ nhận được 0,0 do: [1] Khi nào screenOrientationlà mặc định, chỉ hasPermanentMenuKeyfalsekhi không xoay. [2] Khi screenOrientationlà phong cảnh, nó rơi vào thế displayMetrics.heightPixels != realDisplayMetrics.heightPixels)khác. [3] Khi nào screenOrientationlà chân dung, hasPermanentMenuKeyfalse.
Trái cây

nó hoạt động, nhưng trên 4.x if (hasPermanentMenuKey) là chất thải, bình luận nó
djdance

Điều kiện của bạn phải là nếu (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1). API 17 là bắt buộc để thực hiện public void getRealMetrics (DisplayMetrics outMetrics). Xem tài liệu: developer.android.com/reference/kotlin/android/util/…
portfoliobuilder

1

Kết hợp câu trả lời từ @egis và những người khác - điều này hoạt động tốt trên nhiều thiết bị, được thử nghiệm trên Pixel EMU, Samsung S6, Sony Z3, Nexus 4. Mã này sử dụng kích thước hiển thị để kiểm tra tính khả dụng của thanh điều hướng và sau đó sử dụng thực tế kích thước thanh điều hướng hệ thống nếu có.

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

}


Nếu tôi muốn lập trình chiều cao của TextView xuống dưới cùng của chiều cao có thể sử dụng được của màn hình, tôi sẽ sử dụng lớp này như thế nào?
Naveed Abbas,

1

Cách lấy chiều cao của thanh điều hướng và thanh trạng thái. Mã này phù hợp với tôi trên một số thiết bị Huawei và thiết bị Samsung . Giải pháp của Egis ở trên là tốt, tuy nhiên, nó vẫn không chính xác trên một số thiết bị. Vì vậy, tôi đã cải thiện nó.

Đây là mã để lấy chiều cao của thanh trạng thái

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
    }

Phương thức này luôn trả về chiều cao của thanh điều hướng ngay cả khi thanh điều hướng bị ẩn.

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

LƯU Ý: trên Samsung A70, phương pháp này trả về chiều cao của thanh trạng thái + chiều cao của thanh điều hướng. Trên các thiết bị khác (Huawei), nó chỉ trả về chiều cao của Thanh điều hướng và trả về 0 khi thanh điều hướng bị ẩn.

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

Đây là mã để lấy chiều cao của thanh điều hướng và thanh trạng thái

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

Tôi đã làm điều này, nó hoạt động trên mọi thiết bị tôi đã thử nghiệm và thậm chí trên trình giả lập:

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

Đây là cách tôi giải quyết vấn đề này. Tôi đã tạo một thanh dưới cùng có thể ẩn, cần đệm tùy thuộc vào việc có thanh điều hướng hay không (điện dung, trên màn hình hoặc chỉ trước kẹo mút).


Lượt xem

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

Trong trường hợp của tôi, nơi tôi muốn có một cái gì đó như thế này:

Ảnh chụp màn hình

Tôi đã phải làm theo điều tương tự như được đề xuất bởi @Mdlc nhưng có lẽ đơn giản hơn một chút ( chỉ nhắm mục tiêu > = 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)

Nó cũng hoạt động trên phong cảnh:

phong cảnh

Chỉnh sửa Giải pháp trên không hoạt động chính xác trong chế độ nhiều cửa sổ trong đó hình chữ nhật có thể sử dụng không nhỏ hơn chỉ do thanh điều hướng mà còn do kích thước cửa sổ tùy chỉnh. Một điều mà tôi nhận thấy là trong nhiều cửa sổ, thanh điều hướng không di chuột qua ứng dụng nên ngay cả khi không có thay đổi nào đối với phần đệm DecorView, chúng tôi vẫn có hành vi chính xác:

Nhiều cửa sổ mà không có thay đổi đối với phần đệm chế độ xem trang trí Một cửa sổ không có thay đổi đối với đệm xem trang trí

Lưu ý sự khác biệt giữa cách thanh điều hướng di chuột qua cuối ứng dụng trong các trường hợp này. May mắn thay, điều này rất dễ sửa chữa. Chúng tôi có thể kiểm tra xem ứng dụng có nhiều cửa sổ hay không. Đoạn mã dưới đây cũng bao gồm phần tính toán và điều chỉnh vị trí của thanh công cụ (giải pháp đầy đủ: 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
    }

Kết quả trên chế độ bật lên của Samsung:

nhập mô tả hình ảnh ở đây


0

Mã đã thử nghiệm để nhận chiều cao của thanh điều hướng (tính bằng 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;
}

Mã đã kiểm tra để nhận chiều cao của thanh trạng thái (tính bằng 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;
}

Chuyển đổi pixel thành dp:

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

0

Trong trường hợp Samsung S8, không có phương pháp nào được cung cấp ở trên cung cấp chiều cao thích hợp của thanh điều hướng, vì vậy tôi đã sử dụng Android nhà cung cấp chiều cao bàn phím KeyboardHeightProvider . Và nó cho tôi chiều cao theo giá trị âm và để định vị bố cục, tôi đã điều chỉnh giá trị đó trong các phép tính.

Đây là 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"/>

Sử dụng trong 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()
    }
}

Để được trợ giúp thêm, bạn có thể xem cách sử dụng nâng cao của điều này tại đây .


0

Giải pháp một dòng đơn giản

Như được gợi ý trong nhiều câu trả lời ở trên, chẳng hạn

Chỉ cần lấy chiều cao của thanh điều hướng có thể là không đủ. Chúng ta cần phải xem xét liệu 1. thanh điều hướng tồn tại, 2. nó ở dưới cùng, hoặc phải hoặc trái, 3. ứng dụng có đang mở ở chế độ nhiều cửa sổ hay không.

May mắn thay, bạn có thể dễ dàng bỏ qua tất cả các đoạn mã dài bằng cách chỉ cần thiết lập android:fitsSystemWindows="true"trong bố cục gốc của bạn. Hệ thống Android sẽ tự động đảm nhận việc thêm các phần đệm cần thiết vào bố cục gốc để đảm bảo rằng các chế độ xem con không đi vào vùng thanh điều hướng hoặc thanh trạng thái.

Có một giải pháp đơn giản một dòng

android:fitsSystemWindows="true"

hoặc theo chương trình

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

bạn cũng có thể xem root bằng cách

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

Để biết thêm chi tiết về cách lấy root-view, hãy tham khảo - https://stackoverflow.com/a/4488149/9640177

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.