一、Activity界面的划分
簡單說明一下(上圖Activity采用默認Style,狀態欄和標題欄都會顯示):最大的草綠色區域是屏幕界面,紅色次大區域我們稱之為“應用程序界面區域”,最小紫色的區域我們稱之為“View繪制區域”;屏幕頂端、應用界面區之外的那部分顯示手機電池網絡運營商信息的為“狀態欄”,應用區域頂端、View繪制區外部顯示Activity名稱的部分我們稱為“標題欄”。
二、狀態高度的測量
狀態欄是顯示顯示手機狀態(如電池電量、網絡狀態、時間、運營商信息等)的區域,一般內容型應用都會顯示保留狀態欄,但是游戲界面如果還保留狀態欄就不合適了,因為游戲界面要響應各種手勢,而狀態欄也會響應一些手勢,有可能引發錯誤操作,所以一般在游戲界面都會隱藏狀態欄,使游戲界面全屏顯示,關於設置全屏請查看
《Android設置Activity全屏的兩種方式及Theme屬性解析》,繼續看狀態欄高度測量:
狀態欄高度的測量我在這里提供4種方法:
(1)通過系統尺寸資源獲取
狀態欄高度定義在Android系統尺寸資源中status_bar_height,但這並不是公開可直接使用的,例如像通常使用系統資源那樣android.R.dimen.status_bar_height。但是系統給我們提供了一個Resource類,通過這個類可以獲取資源文件,借此可以獲取到status_bar_height
/** * 獲取狀態欄高度——方法1 * */ int statusBarHeight1 = -1; //獲取status_bar_height資源的ID int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { //根據資源ID獲取響應的尺寸值 statusBarHeight1 = getResources().getDimensionPixelSize(resourceId); } Log.e("-------", "狀態欄-方法1:" + statusBarHeight1)
(2)通過R類的反射
大家都知道Android的所有資源都會有惟一標識在R類中作為引用。我們也可以通過反射獲取R類的實例域,然后找status_bar_height
/** * 獲取狀態欄高度——方法2 * */ int statusBarHeight2 = -1; try { Class<?> clazz = Class.forName("com.android.internal.R$dimen"); Object object = clazz.newInstance(); int height = Integer.parseInt(clazz.getField("status_bar_height") .get(object).toString()); statusBarHeight2 = getResources().getDimensionPixelSize(height); } catch (Exception e) { e.printStackTrace(); } Log.e("-------", "狀態欄-方法2:" + statusBarHeight2);
(3)借助應用區域的top屬性
這就用到了開題時的那張屏幕區域划分圖片,狀態欄位於屏幕最頂端,其位置從(0,0)開始,故而應用區域的頂端位置(高度 = Y坐標 - 0)即為狀態欄的高度
/** * 獲取狀態欄高度——方法3 * 應用區的頂端位置即狀態欄的高度 * *注意*該方法不能在初始化的時候用 * */ Rect rectangle= new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(rectangle); //高度為rectangle.top-0仍為rectangle.top Log.e("-------", "狀態欄-方法3:" + rectangle.top);
*注意*
如果單單獲取statusBar高度而不獲取titleBar高度時,這種方法並不推薦大家使用,因為這種方法依賴於WMS(窗口管理服務的回調)。正是因為窗口回調機制,所以在Activity初始化時執行此方法得到的高度是0,這就是很多人獲取到statusBar高度為0的原因。這個方法推薦在回調方法onWindowFocusChanged()中執行,才能得到預期結果。
(4)借助屏幕和應用區域高度
還是看屏幕區域划分圖,是不是狀態欄占滿了屏幕中除應用區域之外的全部呢?所以直接上代碼
/** * 獲取狀態欄高度——方法4 * 狀態欄高度 = 屏幕高度 - 應用區高度 * *注意*該方法不能在初始化的時候用 * */ //屏幕 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); //應用區域 Rect outRect1 = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect1); int statusBar = dm.heightPixels - outRect1.height(); //狀態欄高度=屏幕高度-應用區域高度 Log.e("------------", "狀態欄-方法4:" + statusBar);
3、4這兩種方法其實本質是一樣,所以如果單單獲取statusBar高度而不獲取titleBar高度時也不推薦大家使用,理由同上方法3
三、標題欄高度的測量
搞懂了上邊的原理,標題欄高度的測量也就手到擒來啦,依舊是屏幕區域划分圖。這里也給出兩種方法,先給出公共代碼吧,就是獲取各區域(*注意*依舊是在Activity的回調方法onWindowFocusChanged()中執行,才能得到預期結果)
//屏幕 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); Log.e("-------", "屏幕高:" + dm.heightPixels); //應用區域 Rect outRect1 = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect1); Log.e("------", "應用區頂部" + outRect1.top); Log.e("-------", "應用區高" + outRect1.height()); //View繪制區域 Rect outRect2 = new Rect(); getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(outRect2); Log.e("--------", "View繪制區域頂部-錯誤方法:" + outRect2.top); //不能像上邊一樣由outRect2.top獲取,這種方式獲得的top是0,可能是bug吧 int viewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop(); //要用這種方法 Log.e("--------", "View繪制區域頂部-正確方法:" + viewTop); Log.e("--------", "View繪制區域高度:" + outRect2.height());
有時候獲取View繪制區時列出來的那個outRect2.top=0
(1)top-top
/** * 獲取標題欄高度-方法1 * 標題欄高度 = View繪制區頂端位置 - 應用區頂端位置(也可以是狀態欄高度,獲取狀態欄高度方法3中說過了) * */ int titleHeight1 = viewTop - outRect1.top; Log.e("--------", "標題欄高度-方法1:" + titleHeight1);
(2)高度-高度
/** * 獲取標題欄高度-方法2 * 標題欄高度 = 應用區高度 - View繪制區高度 * */ int titleHeight2 = outRect1.height() - outRect2.height(); Log.e("----------", "標題欄高度-方法2:" + titleHeight2);
四、注意事項
*注意*
(1)不管你是否設置全屏模式,或是不顯示標題欄,在使用獲取狀態欄高度方法1和獲取狀態欄高度方法2都會測量到狀態欄的高度,理解原理就不難解釋——系統資源屬性是固定的、真實的,不管你是否隱瞞(隱藏或者顯示),它都在那里;
(2)但是若使用獲取狀態欄高度方法3和獲取狀態欄高度方法4,以及獲取標題欄高度方法1和獲取標題欄高度方法2,都是依賴於WMS,是在界面構建后根據View獲取的,所以顯示了就有高度,不顯示自然沒高度了。
下面是驗證
先設置Activity為全屏
<activity android:name=".MainActivity" android:label="@string/app_name" android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
屏幕各區域獲取不變;
輸出StatusBar和titleBar高度信息
int titleHeight1 = viewTop - outRect1.top; Log.e("--------", "驗證Statue高度:" + titleHeight1); Log.e("--------", "驗證Title高度:" + outRect1.top);