프로그래밍 방식으로 Android 탐색 모음의 높이와 너비를 어떻게 얻습니까?


122

화면 하단의 검은 색 탐색 표시 줄은 Android에서 쉽게 제거 할 수 없습니다. 3.0부터 하드웨어 버튼을 대체하기 위해 Android의 일부였습니다. 다음은 사진입니다.

시스템 표시 줄

이 UI 요소의 너비와 높이의 크기를 픽셀 단위로 구하려면 어떻게해야합니까?


이 문제에 대한 최상의 솔루션이 여기에 추가됩니다. 디스플레이 메트릭과 실제 메트릭을 비교하여 탐색 모음이 장치에 있는지 식별 할 수 있습니다. 내 대답을 봐, 전체 안드로이드 장치의 실제 탐색 모음 크기를 찾기 위해 전체 코드를 추가했습니다.
중국 주권

답변:


178

아래 코드를 시도하십시오.

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

7
감사. +1. 이 접근 방식이 얼마나 안정적인지 아십니까? 리소스 식별자가 다른 플랫폼 버전에서 변경 될 수 있습니까?
Ben Pearson

59
이 코드는 탐색 모음이없는 장치에서는 0을 반환하지 않습니다. 삼성 S2 및 S3에서 테스트되었습니다. 72와 96을 받았습니다.
Egis

4
@Egidijus 내 대답을보세요. 물리적 탐색이있는 장치의 경우 0이 반환됩니다. stackoverflow.com/a/29938139/1683141
Mdlc

1
galaxy s6 edge도 0을 반환하지 않지만 하단에 탐색 표시 줄이 없습니다. 192를 반환합니다.
agamov

5
이 코드는 내비게이션 바 의 기본 크기를 보여줍니다 ( navigation_bar_height_landscape가로 모드의 내비게이션 바 높이와 navigation_bar_width수직 내비게이션 바의 너비 도 시도해보십시오 ). 예를 들어 물리적 메뉴 버튼이 있는지 테스트하여 탐색 모음이 실제로 표시되는지 여부와 위치를 별도로 찾아야합니다. android.googlesource.com/platform/frameworks/base/+/…
user149408

103

앱에서 사용 가능한 화면 크기와 실제 화면 크기를 비교하여 내비게이션 막대 크기를 얻습니다. 앱에서 사용할 수있는 화면 크기가 실제 화면 크기보다 작을 때 내비게이션 바가 있다고 가정합니다. 그런 다음 내비게이션 막대 크기를 계산합니다. 이 방법은 API 14 이상에서 작동합니다.

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

최신 정보

디스플레이 컷 아웃을 고려하는 솔루션에 대해서는 John의 답변을 확인하십시오 .


2
이것은 내가 테스트 한 모든 장치에서 나를 위해 작동하는 유일한 솔루션입니다. 내 요구 사항은 방향을 고려하여 navBar가 화면 하단에 있는지 확인하는 것입니다. 화면 하단의 탐색 모음 크기를 반환하기 위해 답변에서 코드를 약간 수정했습니다. 0은 해당 코드가 없음을 나타냅니다. 내 테스트 결과 Nexus5 = 세로 : 144, 가로 : 0 | Nexus7 = 세로 : 96, 가로 : 96 | Samsung Note II = 세로 : 0, 가로 : 0 | 삼성 S10 = 세로 : 0, 가로 : 0
se22as

2
또한 Danylo의 솔루션이 일부 모델에서만 작동하는지 확인할 수 있습니다. Egidijus의이 솔루션은 더 나은 작동 솔루션이며 지금까지 테스트 된 모든 모델에서 작동했습니다. 감사합니다.
Bisclavret

2
상태 표시 줄은 앱 사용 가능 화면의 일부입니다.
이지스

1
이것은 작동하지만 기존 막대가 숨겨져 있는지 여부는 고려하지 않습니다. 이것을 확인하는 방법을 아십니까?
X-HuMan

1
내비게이션 바 대신 내비게이션 제스처가 켜져있는 동안에는 Android P에서 작동하지 않습니다. 이 상황에 대한 해결 방법을 안내해 주시겠습니까
Nik

36

NavigationBar 높이는 장치에 따라 다르지만 방향에 따라 다릅니다. 먼저 장치에 navbar가 있는지 확인한 다음 장치가 태블릿인지 아니면 태블릿이 아닌지 (휴대폰) 마지막으로 올바른 높이를 얻으려면 장치의 방향을 확인해야합니다.

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

이것은 나를 위해 작동하지 않으며 Nexus5는 hasMenuKey 및 hasBackKey에 대해 true를 반환하므로 navBar가 없다고 생각합니다. (Nexus7 제대로 hasMenuKey 및 hasBackKey에 대해 false를 반환)
se22as

5
Nexus 5에서이 코드를 테스트했으며 둘 다에 대해 false를 반환합니다. 에뮬레이터 또는 ROM이 아닌 것이 확실합니까?
Mdlc 2015-06-10

내가 에뮬레이터를 사용하고 있었다 네, 죄송합니다 내가 이전에 내 성명을 명확히해야
se22as

이건! hasMenuKey가 아니야 || ! hasMenuKey &&! hasBackKey 대신! hasBackKey? 확인하시기 바랍니다
yongsunCN

2
불행히도 Sony Xperia Z3에서는 작동하지 않지만 hasBackKey = false!사실입니다. 다음은 대신했다 : boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
Hamzeh Soboh에게

27

실제로 태블릿 (최소 Nexus 7)의 내비게이션 바는 세로와 가로에서 크기가 다르므로이 기능은 다음과 같이 표시됩니다.

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
컨텍스트에서 방향을 검색하려면 다음을 사용하십시오.context.getResources().getConfiguration().orientation
Hugo Gresse

이 문제에 대한 최상의 솔루션이 여기에 추가됩니다. 디스플레이 메트릭과 실제 메트릭을 비교하여 탐색 모음이 장치에 있는지 식별 할 수 있습니다. 내 대답을 봐, 전체 안드로이드 장치의 실제 탐색 모음 크기를 찾기 위해 전체 코드를 추가했습니다.
Sino Raj

17

더 나은 답이 여기에 있다고 생각합니다. 왜냐하면 컷 아웃 높이도 얻을 수 있기 때문입니다.

루트 뷰를 가져 와서 setOnApplyWindowInsetsListener를 추가하고 (또는 여기에서 onApplyWindowInsets를 재정의 할 수 있음) 여기에서 insets.getSystemWindowInsets를 가져옵니다.

내 카메라 활동에서 systemWindowInsetBottom과 동일한 패딩을 하단 레이아웃에 추가합니다. 마지막으로 컷 아웃 문제를 수정합니다.

카메라 활동 삽입

appcompat을 사용하면 다음과 같습니다.

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

appcompat이 없으면 다음과 같습니다.

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

setOnApplyWindowInsetsListener대신 사용할 수 있습니까? 그렇다면 어떻게? 그리고 왜 LOLLIPOP과 나머지를 구별 했습니까?
안드로이드 개발자

예, 가능하고 더 좋습니다. 답을 고칠 게요.
John

1
이것이 최고의 답변입니다. 나는 말 그대로 당신을 사랑합니다.
Himanshu Rawat

1
이것이 진정한 답입니다.
nyconing

1
리스너는 onCreate ()에서 호출 할 때 실행되지 않습니다. 내가 뭔가를 놓치고 있습니까?
howettl

12

도움이 되었기를 바랍니다.

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

이것은 탐색 모음을 피하기 위해 paddingRight 및 paddingBottom을 View에 추가하는 코드입니다. 여기에 몇 가지 답변을 결합하고 isInMultiWindowMode와 함께 가로 방향에 대한 특수 절을 만들었습니다. 핵심은 navigation_bar_height 를 읽고 config_showNavigationBar 도 확인하는 것입니다. 를 확인하여 실제로 높이를 사용해야하는지 확인합니다.

이전 솔루션 중 어느 것도 나를 위해 일하지 않았습니다. Android 7.0부터는 다중 창 모드를 고려해야합니다. realSize 는 전체 화면 (두 분할 창 모두)의 크기를 제공 하고 크기 는 앱 창의 크기 만 제공 하므로 display.realSizedisplay.size를 비교하는 구현이 중단 됩니다. 패딩을이 차이로 설정하면 전체 뷰가 패딩이됩니다.

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

하단 탐색 메뉴의 높이는 48dp (세로 및 가로 모드 모두)이며 막대가 세로로 배치 될 때 42dp입니다.


4
높이는 NORMALLY 48dp입니다. 그러나 일부 제조업체는이를 변경할 수 있고 일부 사용자 지정 ROM은이를 변경할 수 있습니다. 정확한 크기를 얻으려면 Danylo 답변 ( stackoverflow.com/a/26118045/1377145 ) 에 의존하는 것이 좋습니다
Hugo Gresse

더 좋습니다. 감사.
Aritra Roy

수직 배치? 가로 모드 일 때처럼?
sudocoder

1

Egidijus가 제안한 솔루션은 Build.VERSION.SDK_INT> = 17에서 완벽하게 작동합니다.

하지만 내 장치에서 Build.VERSION.SDK_INT <17로 다음 문을 실행하는 동안 "NoSuchMethodException"이 발생했습니다.

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

이러한 경우 getRealScreenSize () 메서드를 수정했습니다.

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

1

모든 기기 (Nexus 5, Samsung Galaxy Nexus 6 edge +, Samsung S10, Samsung Note II 등)에서이 문제를 해결했습니다. 기기 관련 문제를 처리하는 데 도움이 될 것 같습니다.

여기에 두 가지 유형의 코드를 추가합니다.

자바 코드 (기본 Android 용) :

import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.ViewConfiguration;
import android.view.WindowManager;

public class DeviceSpec {

    private int resourceID = -1;
    private Display display = null;
    private DisplayMetrics displayMetrics = null;
    private DisplayMetrics realDisplayMetrics = null;
    private Resources resources = null;
    private WindowManager windowManager = null;

    public double GetNavigationBarHeight(Context context) {
        try {
            windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            display = windowManager.getDefaultDisplay();
            displayMetrics = new DisplayMetrics();
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                realDisplayMetrics = new DisplayMetrics();
                display.getMetrics(displayMetrics);
                display.getRealMetrics(realDisplayMetrics);
                if(displayMetrics.heightPixels != realDisplayMetrics.heightPixels) {
                    resources = context.getResources();
                    return GetNavigationBarSize(context);
                }
            }
            else {
                resources = context.getResources();
                resourceID = resources.getIdentifier("config_showNavigationBar", "bool", "android");
                if (resourceID > 0 && resources.getBoolean(resourceID))
                    return GetNavigationBarSize(context);
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return 0;
    }

    private double GetNavigationBarSize(Context context) {
        resourceID = resources.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceID > 0 && ViewConfiguration.get(context).hasPermanentMenuKey())
           return (resources.getDimensionPixelSize(resourceID) / displayMetrics.density);
        return 0;
    }
}

및 C # 코드 (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()필요 API 레벨 17
Weizhi

(Build.VERSION.SdkInt> = BuildVersionCodes.JellyBeanMr2)가 이미 선택된 경우.
Sino Raj

코드를 수정해야합니다. 내 LG Nexus 5X는 다음과 같은 이유로 0.0을 얻습니다. [1] screenOrientation기본값 hasPermanentMenuKey은 언제 false회전하지 않는 경우에만입니다. [2] screenOrientation풍경 일 때는 displayMetrics.heightPixels != realDisplayMetrics.heightPixels)다른 곳 으로 떨어집니다 . [3] 인 screenOrientation경우 hasPermanentMenuKeyfalse입니다.
Fruit

그것은 작동하지만, 4.x의에 (hasPermanentMenuKey)는 폐기물, 의견이있는 경우
djdance

조건은 if (Build.VERSION.SDK_INT> = Build.VERSION_CODES.JELLY_BEAN_MR1)이어야합니다. public void getRealMetrics (DisplayMetrics outMetrics)를 수행하려면 API 17이 필요합니다. 문서 참조 : developer.android.com/reference/kotlin/android/util/…
Portfoliobuilder

1

@egis 등의 답변을 결합하면 다양한 장치에서 잘 작동하며 Pixel EMU, Samsung S6, Sony Z3, Nexus 4에서 테스트되었습니다.이 코드는 디스플레이 크기를 사용하여 탐색 모음의 가용성을 테스트 한 다음 실제 시스템 탐색 모음 크기 (있는 경우).

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

}


프로그래밍 방식으로 TextView의 높이를 화면의 사용 가능한 높이의 맨 아래로 설정하려면이 클래스를 어떻게 사용합니까?
Naveed 압바스

1

탐색 표시 줄 및 상태 표시 줄의 높이를 가져 오는 방법. 이 코드는 일부 Huawei 장치 및 삼성 장치에서 작동합니다. . 위의 Egis의 솔루션은 좋지만 일부 장치에서는 여전히 올바르지 않습니다. 그래서 개선했습니다.

상태 표시 줄의 높이를 가져 오는 코드입니다.

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
    }

이 메서드는 탐색 모음이 숨겨져 있어도 항상 탐색 모음의 높이를 반환합니다.

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

참고 : Samsung A70에서이 메서드는 상태 표시 줄의 높이 + 탐색 표시 줄의 높이를 반환합니다. 다른 장치 (Huawei)에서는 탐색 모음의 높이 만 반환하고 탐색 모음이 숨겨져 있으면 0을 반환합니다.

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

탐색 표시 줄과 상태 표시 줄의 높이를 가져 오는 코드입니다.

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

이 작업을 수행했으며 테스트 한 모든 장치와 에뮬레이터에서도 작동합니다.

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

이 문제를 해결 한 방법은 다음과 같습니다. 내비게이션 바가 있는지 여부에 따라 패딩이 필요한 숨길 수있는 하단 바를 만들었습니다 (용량 성, 화면 상 또는 사전 롤리팝).


전망

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

내 경우에는 다음과 같은 것을 원했습니다.

스크린 샷

@Mdlc가 제안한 것과 동일한 것을 따라야 했지만 아마도 약간 더 간단합니다 ( > = 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)

풍경에서도 작동합니다.

경치

편집하다 위의 솔루션은 탐색 모음뿐만 아니라 사용자 지정 창 크기 때문에 사용 가능한 사각형이 더 작은 다중 창 모드에서 제대로 작동하지 않습니다. 내가 알아 차린 한 가지는 다중 창에서 탐색 모음이 앱 위로 이동하지 않으므로 DecorView 패딩을 변경하지 않아도 올바른 동작을 보입니다.

데코 뷰 패딩을 변경하지 않은 다중 창 데코 뷰 패딩을 변경하지 않은 단일 창

이러한 시나리오에서 내비게이션 바가 앱 하단을 가리킬 때의 차이에 유의하십시오. 다행히도 이것은 쉽게 고칠 수 있습니다. 앱이 멀티 윈도우인지 확인할 수 있습니다. 아래 코드에는 툴바 위치를 계산하고 조정하는 부분도 포함되어 있습니다 (전체 솔루션 : 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
    }

삼성 팝업 모드의 결과 :

여기에 이미지 설명 입력


0

탐색 막대의 높이 (픽셀 단위)를 가져 오는 테스트 된 코드 :

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

상태 표시 줄의 높이 (픽셀)를 가져 오는 테스트 된 코드 :

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

픽셀을 dp로 변환 :

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

0

Samsung S8의 경우 위에 제공된 방법 중 어느 것도 탐색 막대의 적절한 높이를 제공하지 않았으므로 KeyboardHeightProvider 키보드 높이 공급자 android를 사용했습니다. . 그리고 그것은 나에게 음수 값으로 높이를 주었고 레이아웃 위치 지정을 위해 계산에서 그 값을 조정했습니다.

여기 있습니다 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"/>

사용 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()
    }
}

추가 도움이 필요하면 여기 에서 고급 사용법을 살펴볼 수 있습니다 .


0

간단한 원 라인 솔루션

위의 많은 답변에서 제안했듯이 예를 들어

단순히 탐색 모음 높이를 얻는 것만으로는 충분하지 않을 수 있습니다. 1. 내비게이션 바가 존재하는지, 2. 하단에 있는지, 오른쪽 또는 왼쪽에 있는지, 3. 앱이 다중 창 모드로 열려 있는지 고려해야합니다.

다행히도 android:fitsSystemWindows="true"루트 레이아웃을 설정 하기 만하면 모든 긴 코딩을 쉽게 건너 뛸 수 있습니다 . Android 시스템은 하위보기가 탐색 표시 줄 또는 상태 표시 줄 영역에 들어 가지 않도록 루트 레이아웃에 필요한 패딩을 자동으로 추가합니다.

간단한 한 줄 솔루션이 있습니다.

android:fitsSystemWindows="true"

또는 프로그래밍 방식으로

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

당신은 또한 루트 뷰를 얻을 수 있습니다

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

루트 뷰 가져 오기에 대한 자세한 내용은 https://stackoverflow.com/a/4488149/9640177을 참조 하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.