Android应用:StatusBar状态栏NavigationBar虚拟按键栏ActionBar标题栏Window屏幕内容区域等的宽高

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android应用:StatusBar状态栏NavigationBar虚拟按键栏ActionBar标题栏Window屏幕内容区域等的宽高相关的知识,希望对你有一定的参考价值。

一、屏幕中各种栏目以及屏幕的尺寸

      当我们需要计算屏幕中一些元素的高度时,或许需要先获取到屏幕或者各种栏目的高度,下面这个类包含了Status bar状态栏,Navigation bar虚拟按键栏,Action bar标题栏, Window屏幕内容等的宽高的计算,可以带来极大的方便。

      因为我在代码中做了比较详尽的注释,在这里不再多阐述,以下是代码:

  1 /**
  2  * 这个类描述了当前设备的配置中system bar的尺寸(StatusBar状态栏,NavigationBar虚拟按键栏,ActionBar标题栏)、
  3  * 屏幕宽高以及一些相关的特征。
  4  */
  5 public static class SystemBarConfig {
  6 
  7     private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
  8     private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height";
  9     private static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape";
 10     private static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width";
 11     private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar";
 12 
 13     private final int mStatusBarHeight;
 14     private final int mActionBarHeight;
 15     private final boolean mHasNavigationBar;
 16     private final int mNavigationBarHeight;
 17     private final int mNavigationBarWidth;
 18     private final int mContentHeight;
 19     private final int mContentWidth;
 20     private final boolean mInPortrait;
 21     private final float mSmallestWidthDp;
 22 
 23     private SystemBarConfig(Activity activity) {
 24         Resources res = activity.getResources();
 25         mInPortrait = (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
 26         mSmallestWidthDp = getSmallestWidthDp(activity);
 27         mStatusBarHeight = getInternalDimensionSize(res, STATUS_BAR_HEIGHT_RES_NAME);
 28         mActionBarHeight = getActionBarHeight(activity);
 29         mNavigationBarHeight = getNavigationBarHeight(activity);
 30         mNavigationBarWidth = getNavigationBarWidth(activity);
 31         mContentHeight = getContentHeight(activity);
 32         mContentWidth = getContentWidth(activity);
 33         mHasNavigationBar = (mNavigationBarHeight > 0);
 34                
 35     }
 36     
 37     // 安卓系统允许修改系统的属性来控制navigation bar的显示和隐藏,此方法用来判断是否有修改过相关属性。
 38     // (修改系统文件,在build.prop最后加入qemu.hw.mainkeys=1即可隐藏navigation bar)
 39     // 相关属性模拟器中有使用。
 40     // 当返回值等于"1"表示隐藏navigation bar,等于"0"表示显示navigation bar。
 41     @TargetApi(19)
 42     private String getNavBarOverride() {
 43         String isNavBarOverride = null;
 44         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
 45             try {
 46                 Class c = Class.forName("android.os.SystemProperties");
 47                 Method m = c.getDeclaredMethod("get", String.class);
 48                 m.setAccessible(true);
 49                 isNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys");
 50             } catch (Throwable e) {
 51                 isNavBarOverride = null;
 52             }
 53         }
 54         return isNavBarOverride;
 55     }
 56 
 57     //通过此方法获取action bar的高度
 58     @TargetApi(14)
 59     private int getActionBarHeight(Context context) {
 60         int result = 0;
 61         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
 62             TypedValue tv = new TypedValue();
 63             context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
 64             result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics());
 65         }
 66         return result;
 67     }
 68     
 69     //通过此方法获取navigation bar的高度
 70     @TargetApi(14)
 71     private int getNavigationBarHeight(Context context) {
 72         Resources res = context.getResources();
 73         int result = 0;
 74         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
 75             if (hasNavBar(context)) {
 76                 String key;
 77                 if (mInPortrait) {
 78                     key = NAV_BAR_HEIGHT_RES_NAME;
 79                 } else {
 80                     key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME;
 81                 }
 82                 return getInternalDimensionSize(res, key);
 83             }
 84         }
 85         return result;
 86     }
 87 
 88     //通过此方法获取navigation bar的宽度
 89     @TargetApi(14)
 90     private int getNavigationBarWidth(Context context) {
 91         Resources res = context.getResources();
 92         int result = 0;
 93         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
 94             if (hasNavBar(context)) {
 95                 return getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME);
 96             }
 97         }
 98         return result;
 99     }
100 
101     //通过此方法判断是否存在navigation bar
102     @TargetApi(14)
103     private boolean hasNavBar(Context context) {
104         Resources res = context.getResources();
105         int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android");
106         if (resourceId != 0) {
107             boolean hasNav = res.getBoolean(resourceId);
108             // 查看是否有通过系统属性来控制navigation bar。
109             if ("1".equals(getNavBarOverride())) {
110                 hasNav = false;
111             } else if ("0".equals(getNavBarOverride())) {
112                 hasNav = true;
113             }
114             return hasNav;
115         } else { 
116             //可通过此方法来查看设备是否存在物理按键(menu,back,home键)。
117             return !ViewConfiguration.get(context).hasPermanentMenuKey();
118         }
119     }
120     
121     //通过此方法获取资源对应的像素值
122     private int getInternalDimensionSize(Resources res, String key) {
123         int result = 0;
124         int resourceId = res.getIdentifier(key, "dimen", "android");
125         if (resourceId > 0) {
126             result = res.getDimensionPixelSize(resourceId);
127         }
128         return result;
129     }
130 
131     //通过此方法获取最小一边的dp值,再通过这个dp值大小来判断设备的navigation bar是显示在底部还是右侧
132     @TargetApi(17)
133     private float getSmallestWidthDp(Activity activity) {
134         DisplayMetrics metrics = new DisplayMetrics();
135         float widthDp;
136         float heightDp;
137         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
138             //API 17之后使用,获取的像素宽高包含虚拟键所占空间,在API 17之前通过反射获取,
139             //获取的屏幕高度包含status bar和navigation bar
140             activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
141             widthDp = metrics.widthPixels / metrics.density;
142             heightDp = metrics.heightPixels / metrics.density;
143         } else {
144             //获取的屏幕高度包含status bar,但不包含navigation bar
145             activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
146             widthDp = metrics.widthPixels / metrics.density;
147             heightDp = (metrics.heightPixels + getNavigationBarWidth(activity))/ metrics.density;
148         }
149         return Math.min(widthDp, heightDp);
150     }
151     
152     //通过此方法获取屏幕高度(不含status bar 和 navigation bar的高度)
153     private int getContentHeight(Activity activity) {
154         DisplayMetrics metrics = new DisplayMetrics();
155         activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
156         return metrics.heightPixels - getStatusBarHeight();
157     }
158     
159     //通过此方法获取屏幕的宽度(不含navigation bar的宽度)
160     private int getContentWidth(Activity activity) {
161         DisplayMetrics metrics = new DisplayMetrics();
162         activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
163         return metrics.widthPixels;
164     }
165 
166     /**
167      * 判断navigation bar 是显示在底部还是显示在右侧
168      *
169      * @return true表示在底部,false表示在右侧
170      */
171     public boolean isNavigationAtBottom() {
172         return (mSmallestWidthDp >= 600 || mInPortrait);
173     }
174 
175     /**
176      * 获取status bar状态栏高度
177      *
178      * @return 状态栏高度的像素值
179      */
180     public int getStatusBarHeight() {
181         return mStatusBarHeight;
182     }
183 
184     /**
185      * 获取action bar的高度
186      *
187      * @return action bar高度的像素值
188      */
189     public int getActionBarHeight() {
190         return mActionBarHeight;
191     }
192 
193     /**
194      * 判断此设备是否有navigation bar虚拟按键栏
195      *
196      * @return true表示有,false表示无
197      */
198     public boolean hasNavigtionBar() {
199         return mHasNavigationBar;
200     }
201 
202     /**
203      * 获取navigation bar虚拟按键栏的高度
204      *
205      * @return 返回navigation bar虚拟按键栏的高度的像素值,如果设备没有navigation bar虚拟按键栏则返回0
206      */
207     public int getNavigationBarHeight() {
208         return mNavigationBarHeight;
209     }
210 
211     /**
212      * 获取navigation bar虚拟按键栏的宽度(当navigation bar虚拟按键栏垂直显示在右侧时使用)
213      *
214      * @return 返回navigation bar虚拟按键栏的宽度的像素值,如果设备没有navigation bar虚拟按键栏则返回0
215      */
216     public int getNavigationBarWidth() {
217         return mNavigationBarWidth;
218     }
219     
220     /**
221      * 获取屏幕高度(不含status bar 和 navigation bar的高度)
222      * 
223      * @return 返回屏幕高度的像素值(不含status bar 和 navigation bar的高度)
224      */
225     public int getContentHeight() {
226         return mContentHeight;
227     }
228     
229     /**
230      * 获取屏幕宽度(不含navigation bar的宽度)
231      * 
232      * @return 返回屏幕宽度的像素值(不含navigation bar的宽度)
233      */
234     public int getContentWidth() {
235         return mContentWidth;
236     }
237 
238 }

 

二、控制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 };

 

以上是关于Android应用:StatusBar状态栏NavigationBar虚拟按键栏ActionBar标题栏Window屏幕内容区域等的宽高的主要内容,如果未能解决你的问题,请参考以下文章

android 状态栏梳理

使用 react-navigation 更改状态栏颜色

Android Cordova 状态栏插件 - “StatusBar.hide();”在哪里去?

React Native中状态栏设置StatusBar

Android 12系统源码_SystemUI系统状态栏StatusBar的创建流程

张高兴的 UWP 开发笔记:手机状态栏 StatusBar