1. 程式人生 > >Android完美獲取狀態列高度、標題欄高度、編輯區域高度的獲取

Android完美獲取狀態列高度、標題欄高度、編輯區域高度的獲取

        日常開發中我們經常會因為繪圖、繪製自定義元件、定位元件或者是計算佈局高度需要減去狀態列高度等需要來獲取Activity介面中狀態列、標題欄的高度。但很多人馬上要拿起來就用還是會遇到各種各樣的問題,一時要查很多資料,很是不爽!今天也是猛然發現這個問題費了不少時間,終於有所收穫,特地記錄下來,希望給遇到同樣問題的你一點幫助,也給以後留個筆記。廢話少說,開始正題吧——

        一、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("WangJ", "狀態列-方法1:" + statusBarHeight1);
        看結果:         狀態列高度-方法1         (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("WangJ", "狀態列-方法2:" + statusBarHeight2);
        看結果:         狀態列高度-方法2

        (3)藉助應用區域的top屬性

        這就用到了開題時的那張螢幕區域劃分圖片,狀態列位於螢幕最頂端,其位置從(0,0)開始,故而應用區域的頂端位置(高度 = Y座標 - 0)即為狀態列的高度:
/**
		 * 獲取狀態列高度——方法3
		 * 應用區的頂端位置即狀態列的高度
		 * *注意*該方法不能在初始化的時候用
		 * */
		Rect rectangle= new Rect();
		getWindow().getDecorView().getWindowVisibleDisplayFrame(rectangle);
		//高度為rectangle.top-0仍為rectangle.top
		Log.e("WangJ", "狀態列-方法3:" + rectangle.top);
        看結果:         狀態列高度-方法3
*注意* 如果單單獲取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("WangJ", "狀態列-方法4:" + statusBar);
        看結果:         狀態列高度-方法4
        有人看完會說,What Are You弄啥嘞,小學數學題!秀智商?——呵呵,是的!其實3、4這兩種方法其實本質是一樣,所以如果單單獲取statusBar高度而不獲取titleBar高度時也不推薦大家使用,理由同上方法3。

        三、標題欄高度的測量

        搞懂了上邊的原理,標題欄高度的測量也就手到擒來啦,依舊是螢幕區域劃分圖。這裡也給出兩種方法,先給出公共程式碼吧,就是獲取各區域(*注意*依舊是在Activity的回撥方法onWindowFocusChanged()中執行,才能得到預期結果):
//螢幕
		DisplayMetrics dm = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		Log.e("WangJ", "螢幕高:" + dm.heightPixels);

		//應用區域
		Rect outRect1 = new Rect();
		getWindow().getDecorView().getWindowVisibleDisplayFrame(outRect1);
		Log.e("WangJ", "應用區頂部" + outRect1.top);
		Log.e("WangJ", "應用區高" + outRect1.height());
		
		//View繪製區域
		Rect outRect2 = new Rect();
		getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(outRect2); 
		Log.e("WangJ", "View繪製區域頂部-錯誤方法:" + outRect2.top);   //不能像上邊一樣由outRect2.top獲取,這種方式獲得的top是0,可能是bug吧
		int viewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();   //要用這種方法
		Log.e("WangJ", "View繪製區域頂部-正確方法:" + viewTop);
		Log.e("WangJ", "View繪製區域高度:" + outRect2.height());
        先看一下資訊:         各區域資訊         以手到擒來了吧,有沒有遇到獲取View繪製區時列出來的那個outRect2.top=0的問題呢?原因我們還沒搞懂呢,哪位大俠搞懂了留言給個貢獻值啊,求教了!

        (1)top-top

        直接看程式碼,文筆不好(其實是懶得寫字):
/**
		 * 獲取標題欄高度-方法1
		 * 標題欄高度 = View繪製區頂端位置 - 應用區頂端位置(也可以是狀態列高度,獲取狀態列高度方法3中說過了)
		 * */
		int titleHeight1 = viewTop - outRect1.top;
		Log.e("WangJ", "標題欄高度-方法1:" + titleHeight1);
        看結果:         title高度-方法1         (你要是也想秀智商,可以吧註釋括號裡的拿出來再做一種方法哦,我就不秀了,再秀你都嫌我噁心了^_^)

        (2)高度-高度

        還是看程式碼,有註釋:
/**
		 * 獲取標題欄高度-方法2
		 * 標題欄高度 = 應用區高度 - View繪製區高度
		 * */
		int titleHeight2 = outRect1.height() - outRect2.height();
		Log.e("WangJ", "標題欄高度-方法2:" + titleHeight2);
        看結果:         title高度-方法2

        四、注意事項

        *注意* 

(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("WangJ", "驗證Statue高度:" + titleHeight1);
Log.e("WangJ", "驗證Title高度:" + outRect1.top);
        看結果:

        
        

        就這麼多把,感覺解釋得挺清楚了。水平有限,歡迎批評指正! 如果對你有幫助,鼓勵一下唄,親!