有没有一种方法可以知道这个导航栏在横屏时会显示在哪里:
- 在Nexus 7上,它显示在底部
- 在Nexus 5上,它显示在右侧
部分基于Pauland的回答(又基于PhoneWindowManager
的实现),以下是我当前使用的代码:
public static boolean isSystemBarOnBottom(Context ctxt) {
Resources res=ctxt.getResources();
Configuration cfg=res.getConfiguration();
DisplayMetrics dm=res.getDisplayMetrics();
boolean canMove=(dm.widthPixels != dm.heightPixels &&
cfg.smallestScreenWidthDp < 600);
return(!canMove || dm.widthPixels < dm.heightPixels);
}
这适用于运行Android 5.1的Nexus 7 2012和Nexus 4。
在具有永久MENU键的设备上,没有系统栏。根据您的使用情况,您可能需要检查这种情况:
ViewConfiguration.get(ctxt).hasPermanentMenuKey()
(其中ctxt
是某个Context
)
个人而言,我使用这个方法来尝试使滑动面板与系统栏相反方向,因为在带有系统栏的侧边触发边缘划痕比较困难。对于任何关键任务,我不会使用此方法或任何其他算法(如依赖于getDecorView()
的算法)。
PhoneWindowManager
使用它来决定在哪里放置系统栏。如果PhoneWindowManager
发生变化,isSystemBarOnBottom()
也必须更改。这都是一个hack,但我们只有这个hack。 - CommonsWarePhoneWindowManager
所声明的那样,那些“返回、主页、最近使用的应用”按钮是导航栏:WindowState mStatusBar = null; WindowState mNavigationBar = null;
此外,即使是导航栏视图的布局文件也被称为navigation_bar.xml。 - Dmitry Gryazinpublic static boolean hasNavBar (Resources resources)
{
int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0)
return resources.getBoolean(id);
else
return false;
}
public static int getNavigationBarHeight (Resources resources)
{
if (!Utils.hasNavBar(resources))
return 0;
int orientation = resources.getConfiguration().orientation;
//Only phone between 0-599 has navigationbar can move
boolean isSmartphone = resources.getConfiguration().smallestScreenWidthDp < 600;
if (isSmartphone && Configuration.ORIENTATION_LANDSCAPE == orientation)
return 0;
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;
}
public static int getNavigationBarWidth (Resources resources)
{
if (!Utils.hasNavBar(resources))
return 0;
int orientation = resources.getConfiguration().orientation;
//Only phone between 0-599 has navigationbar can move
boolean isSmartphone = resources.getConfiguration().smallestScreenWidthDp < 600;
if (orientation == Configuration.ORIENTATION_LANDSCAPE && isSmartphone)
{
int id = resources.getIdentifier("navigation_bar_width", "dimen", "android");
if (id > 0)
return resources.getDimensionPixelSize(id);
}
return 0;
}
通过使用装饰视图的属性与当前DisplayMetrics相结合,您可以找出导航栏位于哪一侧。
// retrieve the position of the DecorView
Rect visibleFrame = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(visibleFrame);
DisplayMetrics dm = getResources().getDisplayMetrics();
// check if the DecorView takes the whole screen vertically or horizontally
boolean isRightOfContent = dm.heightPixels == visibleFrame.bottom;
boolean isBelowContent = dm.widthPixels == visibleFrame.right;
对我有效的解决方案是:
public static boolean hasNavBar(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point realPoint = new Point();
Display display = wm.getDefaultDisplay();
display.getRealSize(realPoint);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
return metrics.heightPixels + metrics.widthPixels != realPoint.y + realPoint.x;
}
public static boolean isSystemBarOnBottom(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point realPoint = new Point();
Display display = wm.getDefaultDisplay();
display.getRealSize(realPoint);
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
Configuration cfg = context.getResources().getConfiguration();
boolean canMove = (metrics.widthPixels != metrics.heightPixels &&
cfg.smallestScreenWidthDp < 600);
return (!canMove || metrics.widthPixels < metrics.heightPixels);
}
所有现有的答案都无法与FLAG_LAYOUT_NO_LIMITS
一起使用。我找到了一个新的解决方案:
@Override
public void onResume() {
super.onResume();
ViewCompat.setOnApplyWindowInsetsListener(requireActivity().getWindow().getDecorView(), (v, insets) -> {
int sysbottom = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
int systop = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
int sysleft = insets.getInsets(WindowInsetsCompat.Type.systemBars()).left;
int sysright = insets.getInsets(WindowInsetsCompat.Type.systemBars()).right;
return insets;
});
}
boolean navBarOnTheBottom(){
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int viewHeight = displaymetrics.heightPixels;
if (bkg.getHeight() == viewHeight)
{
Log.d(TAG, "nav bar on the side");
return false;
}
else{
Log.d(TAG, "nav bar on the bottom");
return true;
}
}
bkg是包含所有应用程序视图的主要LinearLayout。
请确保bkg.getHeight()不会返回0,因为在某些布局中它会返回0。
编辑:
如果上面的方法返回0,请使用以下方法获取布局高度。@Override
public void onWindowFocusChanged (boolean hasFocus) {
// the height will be set at this point
bkgHeight = bkg.getHeight();
}
DisplayListener
在Android 7.1及以上版本上检查180°屏幕更改,例如:如何检测从横向到横向方向的屏幕旋转180度?。DisplayManager.DisplayListener displayListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
displayListener = new DisplayManager.DisplayListener() {
private int lastRotation =
((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getRotation();
@Override
public void onDisplayAdded(int displayId) {
}
@Override
public void onDisplayChanged(int displayId) {
int rotation = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getRotation();
if (rotation == Surface.ROTATION_90
&& lastRotation == Surface.ROTATION_270
|| rotation == Surface.ROTATION_270
&& lastRotation == Surface.ROTATION_90) {
onNavigationBarMoved(true);
}
lastRotation = rotation;
}
@Override
public void onDisplayRemoved(int displayId) {
}
};
DisplayManager displayManager =
(DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
displayManager.registerDisplayListener(displayListener, null);
onNavigationBarMoved(false);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
onNavigationBarMoved(false);
}
@Override
protected void onDestroy() {
if (displayListener != null) {
DisplayManager displayManager =
(DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
displayManager.unregisterDisplayListener(displayListener);
}
super.onDestroy();
}
protected void onNavigationBarMoved(boolean landscapeScreenRotation) {
boolean leftSideNavigationBar = Build.VERSION.SDK_INT > Build.VERSION_CODES.N
&& ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getRotation() == Surface.ROTATION_270;
// add whatever else you need here
}
onNavigationBarMoved
中,landscapeScreenRotation
的值将告诉您何时发生这种情况,以便您相应地调整UI。
fitsSystemWindows=true
失败时,因为它只会添加填充而不是边距。 - 0101100101