感谢IT大道,copy只为了防止原文被删。原文请看 http://www.itdadao.com/2016/02/07/353404/
一、屏幕中各种栏目以及屏幕的尺寸
当我们需要计算屏幕中一些元素的高度时,或许需要先获取到屏幕或者各种栏目的高度,下面这个类包含了Status bar状态栏,Navigation bar虚拟按键栏,Action bar标题栏, Window屏幕内容等的宽高的计算,可以带来极大的方便。
因为我在代码中做了比较详尽的注释,在这里不再多阐述,以下是代码:
package com.felix.navtest; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.ViewConfiguration; import java.lang.reflect.Method; /** 2 * 这个类描述了当前设备的配置中system bar的尺寸(StatusBar状态栏,NavigationBar虚拟按键栏,ActionBar标题栏)、 3 * 屏幕宽高以及一些相关的特征。 4 */ public class SystemBarConfig { public static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height"; public static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height"; public static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape"; public static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width"; public static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar"; public final int mStatusBarHeight; public final int mActionBarHeight; public final boolean mHasNavigationBar; public final int mNavigationBarHeight; public final int mNavigationBarWidth; public final int mContentHeight; public final int mContentWidth; public final boolean mInPortrait; public final float mSmallestWidthDp; public SystemBarConfig(Activity activity) { Resources res = activity.getResources(); mInPortrait = (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT); mSmallestWidthDp = getSmallestWidthDp(activity); mStatusBarHeight = getInternalDimensionSize(res, STATUS_BAR_HEIGHT_RES_NAME); mActionBarHeight = getActionBarHeight(activity); mNavigationBarHeight = getNavigationBarHeight(activity); mNavigationBarWidth = getNavigationBarWidth(activity); mContentHeight = getContentHeight(activity); mContentWidth = getContentWidth(activity); mHasNavigationBar = (mNavigationBarHeight > 0); } // 安卓系统允许修改系统的属性来控制navigation bar的显示和隐藏,此方法用来判断是否有修改过相关属性。 // (修改系统文件,在build.prop最后加入qemu.hw.mainkeys=1即可隐藏navigation bar) // 相关属性模拟器中有使用。 // 当返回值等于"1"表示隐藏navigation bar,等于"0"表示显示navigation bar。 @TargetApi(19) public String getNavBarOverride() { String isNavBarOverride = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { Class c = Class.forName("android.os.SystemProperties"); Method m = c.getDeclaredMethod("get", String.class); m.setAccessible(true); isNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys"); } catch (Throwable e) { isNavBarOverride = null; } } return isNavBarOverride; } //通过此方法获取action bar的高度 @TargetApi(14) public int getActionBarHeight(Context context) { int result = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { TypedValue tv = new TypedValue(); context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true); result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics()); } return result; } //通过此方法获取navigation bar的高度 @TargetApi(14) public int getNavigationBarHeight(Context context) { Resources res = context.getResources(); int result = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { if (hasNavBar(context)) { String key; if (mInPortrait) { key = NAV_BAR_HEIGHT_RES_NAME; } else { key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME; } return getInternalDimensionSize(res, key); } } return result; } //通过此方法获取navigation bar的宽度 @TargetApi(14) public int getNavigationBarWidth(Context context) { Resources res = context.getResources(); int result = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { if (hasNavBar(context)) { return getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME); } } return result; } //通过此方法判断是否存在navigation bar @TargetApi(14) public boolean hasNavBar(Context context) { Resources res = context.getResources(); int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android"); if (resourceId != 0) { boolean hasNav = res.getBoolean(resourceId); // 查看是否有通过系统属性来控制navigation bar。 if ("1".equals(getNavBarOverride())) { hasNav = false; } else if ("0".equals(getNavBarOverride())) { hasNav = true; } return hasNav; } else { //可通过此方法来查看设备是否存在物理按键(menu,back,home键)。 return !ViewConfiguration.get(context).hasPermanentMenuKey(); } } //通过此方法获取资源对应的像素值 public int getInternalDimensionSize(Resources res, String key) { int result = 0; int resourceId = res.getIdentifier(key, "dimen", "android"); if (resourceId > 0) { result = res.getDimensionPixelSize(resourceId); } return result; } //通过此方法获取最小一边的dp值,再通过这个dp值大小来判断设备的navigation bar是显示在底部还是右侧 @TargetApi(17) public float getSmallestWidthDp(Activity activity) { DisplayMetrics metrics = new DisplayMetrics(); float widthDp; float heightDp; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { //API 之后使用,获取的像素宽高包含虚拟键所占空间,在API 之前通过反射获取, //获取的屏幕高度包含status bar和navigation bar activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics); widthDp = metrics.widthPixels / metrics.density; heightDp = metrics.heightPixels / metrics.density; } else { //获取的屏幕高度包含status bar,但不包含navigation bar activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); widthDp = metrics.widthPixels / metrics.density; heightDp = (metrics.heightPixels + getNavigationBarWidth(activity)) / metrics.density; } return Math.min(widthDp, heightDp); } //通过此方法获取屏幕高度(不含status bar 和 navigation bar的高度) public int getContentHeight(Activity activity) { DisplayMetrics metrics = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); return metrics.heightPixels - getStatusBarHeight(); } //通过此方法获取屏幕的宽度(不含navigation bar的宽度) public int getContentWidth(Activity activity) { DisplayMetrics metrics = new DisplayMetrics(); activity.getWindowManager().getDefaultDisplay().getMetrics(metrics); return metrics.widthPixels; } /** * 判断navigation bar 是显示在底部还是显示在右侧 * * @return true表示在底部,false表示在右侧 */ public boolean isNavigationAtBottom() { return (mSmallestWidthDp >=600 || mInPortrait); } /** * 获取status bar状态栏高度 * * @return 状态栏高度的像素值 */ public int getStatusBarHeight() { return mStatusBarHeight; } /** * 获取action bar的高度 * * @return action bar高度的像素值 */ public int getActionBarHeight() { return mActionBarHeight; } /** * 判断此设备是否有navigation bar虚拟按键栏 * * @return true表示有,false表示无 */ public boolean hasNavigtionBar() { return mHasNavigationBar; } /** * 获取navigation bar虚拟按键栏的高度 * * @return 返回navigation bar虚拟按键栏的高度的像素值,如果设备没有navigation bar虚拟按键栏则返回0 */ public int getNavigationBarHeight() { return mNavigationBarHeight; } /** * 获取navigation bar虚拟按键栏的宽度(当navigation bar虚拟按键栏垂直显示在右侧时使用) * * @return 返回navigation bar虚拟按键栏的宽度的像素值,如果设备没有navigation bar虚拟按键栏则返回0 */ public int getNavigationBarWidth() { return mNavigationBarWidth; } /** * 获取屏幕高度(不含status bar 和 navigation bar的高度) * * @return 返回屏幕高度的像素值(不含status bar 和 navigation bar的高度) */ public int getContentHeight() { return mContentHeight; } /** * 获取屏幕宽度(不含navigation bar的宽度) * * @return 返回屏幕宽度的像素值(不含navigation bar的宽度) */ public int getContentWidth() { return mContentWidth; } }
二、控制Navigation Bar的显示和隐藏
在Android4.4.2(KITKAT<Build.VERSION_CODES.KITKAT>)之前,只能设置:
1)View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
其缺点是当Touch Screen时,Navigation bar将显示出来。
从Android4.4.2起,可以设置:
1)View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
2)View.SYSTEM_UI_FLAG_IMMERSIVE
同时设置以上两个参数,即使Touch Screen时,Navigation bar也不会显示出来。
实现代码:
1 private static Handler sHandler; 2 3 protected void onCreate(Bundle savedInstanceState) { 4 super.onCreate(savedInstanceState); 5 6 sHandler = new Handler(); 7 8 sHandler.post(mHideRunnable); // hide the navigation bar 9 10 final View decorView = getWindow().getDecorView(); 11 decorView 12 .setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { 13 @Override 14 public void onSystemUiVisibilityChange(int visibility) { 15 sHandler.post(mHideRunnable); // hide the navigation bar 16 } 17 }); 18 } 19 20 Runnable mHideRunnable = new Runnable() { 21 @Override 22 public void run() { 23 int flags; 24 int curApiVersion = android.os.Build.VERSION.SDK_INT; 25 // This work only for android 4.4+ 26 if (curApiVersion >= Build.VERSION_CODES.KITKAT) { 27 // This work only for android 4.4+ 28 // hide navigation bar permanently in android activity 29 // touch the screen, the navigation bar will not show 30 flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 31 | View.SYSTEM_UI_FLAG_IMMERSIVE 32 | View.SYSTEM_UI_FLAG_FULLSCREEN; 33 34 } else { 35 // touch the screen, the navigation bar will show 36 flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 37 } 38 39 // must be executed in main thread :) 40 getWindow().getDecorView().setSystemUiVisibility(flags); 41 } 42 };