답변:
아래 코드를 시도하십시오.
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
}
return 0;
navigation_bar_height_landscape
가로 모드의 내비게이션 바 높이와 navigation_bar_width
수직 내비게이션 바의 너비 도 시도해보십시오 ). 예를 들어 물리적 메뉴 버튼이 있는지 테스트하여 탐색 모음이 실제로 표시되는지 여부와 위치를 별도로 찾아야합니다. android.googlesource.com/platform/frameworks/base/+/…
앱에서 사용 가능한 화면 크기와 실제 화면 크기를 비교하여 내비게이션 막대 크기를 얻습니다. 앱에서 사용할 수있는 화면 크기가 실제 화면 크기보다 작을 때 내비게이션 바가 있다고 가정합니다. 그런 다음 내비게이션 막대 크기를 계산합니다. 이 방법은 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의 답변을 확인하십시오 .
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;
}
hasBackKey = false!
사실입니다. 다음은 대신했다 : boolean navBarExists = getResources().getBoolean(getResources().getIdentifier("config_showNavigationBar", "bool", "android"));
실제로 태블릿 (최소 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;
}
context.getResources().getConfiguration().orientation
더 나은 답이 여기에 있다고 생각합니다. 왜냐하면 컷 아웃 높이도 얻을 수 있기 때문입니다.
루트 뷰를 가져 와서 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과 나머지를 구별 했습니까?
도움이 되었기를 바랍니다.
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;
}
이것은 탐색 모음을 피하기 위해 paddingRight 및 paddingBottom을 View에 추가하는 코드입니다. 여기에 몇 가지 답변을 결합하고 isInMultiWindowMode와 함께 가로 방향에 대한 특수 절을 만들었습니다. 핵심은 navigation_bar_height 를 읽고 config_showNavigationBar 도 확인하는 것입니다. 를 확인하여 실제로 높이를 사용해야하는지 확인합니다.
이전 솔루션 중 어느 것도 나를 위해 일하지 않았습니다. Android 7.0부터는 다중 창 모드를 고려해야합니다. realSize 는 전체 화면 (두 분할 창 모두)의 크기를 제공 하고 크기 는 앱 창의 크기 만 제공 하므로 display.realSize 와 display.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);
}
하단 탐색 메뉴의 높이는 48dp (세로 및 가로 모드 모두)이며 막대가 세로로 배치 될 때 42dp입니다.
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();
}
모든 기기 (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
screenOrientation
기본값 hasPermanentMenuKey
은 언제 false
회전하지 않는 경우에만입니다. [2] screenOrientation
풍경 일 때는 displayMetrics.heightPixels != realDisplayMetrics.heightPixels)
다른 곳 으로 떨어집니다 . [3] 인 screenOrientation
경우 hasPermanentMenuKey
는 false
입니다.
@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);
}
}
탐색 표시 줄 및 상태 표시 줄의 높이를 가져 오는 방법. 이 코드는 일부 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
}
이 작업을 수행했으며 테스트 한 모든 장치와 에뮬레이터에서도 작동합니다.
// 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());
}
이 문제를 해결 한 방법은 다음과 같습니다. 내비게이션 바가 있는지 여부에 따라 패딩이 필요한 숨길 수있는 하단 바를 만들었습니다 (용량 성, 화면 상 또는 사전 롤리팝).
전망
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;
}
내 경우에는 다음과 같은 것을 원했습니다.
@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
}
삼성 팝업 모드의 결과 :
탐색 막대의 높이 (픽셀 단위)를 가져 오는 테스트 된 코드 :
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);
}
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()
}
}
간단한 원 라인 솔루션
위의 많은 답변에서 제안했듯이 예를 들어
단순히 탐색 모음 높이를 얻는 것만으로는 충분하지 않을 수 있습니다. 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을 참조 하십시오.