鍍金池/ 教程/ Android/ 獲得屏幕物理尺寸、密度及分辨率
多分辨率適配常用目錄
Android 開發(fā)環(huán)境(Eclipse+ADT+Android 5.0)
Android 原型設(shè)計(jì)工具探索
Makefile 快速入門
Android Studio的NDK開發(fā)
人臉檢測(cè)-靜態(tài)
getprop 與 dumpsys 命令
Maven 編譯開源二維碼掃描項(xiàng)目 zxing
畫布 Canvas
組合控件
Linux 下的模擬器硬件加速
讀取 Excel
android.hardware.camera2 使用指南
橫豎屏切換
Ubuntu 下切換 JDK 版本
拍照和錄像 with Camera
文本與布局
按鈕控制 ViewPager 的左右翻頁(yè)
用 TableLayout 偽裝表格顯示數(shù)據(jù)
Preference Activity 使用詳解
模擬器如何重啟?試試 Genymotion!
獲得屏幕物理尺寸、密度及分辨率
語(yǔ)音識(shí)別
了解 native activity
Android Studio 導(dǎo)入第三方類庫(kù)、jar 包和 so 庫(kù)
啟動(dòng)另一個(gè) App/apk 中的 Activity
APK 簽名
兩個(gè)開源的圖表/報(bào)表控件
android studio 導(dǎo)出 jar 包(Module)并獲得手機(jī)信息
圖片的 Base64 編解碼
混淆與反編譯
Android Studio 和 Gradle
Android 5.1 SDK 下載與配置
persistableMode 與 Activity 的持久化
adb 取出安裝在手機(jī)中的 apk
Android Studio 中的源代碼管理
Handler 使用中可能引發(fā)的內(nèi)存泄漏

獲得屏幕物理尺寸、密度及分辨率

目錄(?)[+]

大家?guī)兔D!

博主參加2014博客之星活動(dòng),大家?guī)兔ν镀崩玻?a rel="nofollow" >猛擊這里!

通過(guò)程序去了解硬件情況是一件十分有意思的事情。很早我就研究在 WM6.5上獲得屏幕物理尺寸,但一直沒(méi)有成功。后來(lái)又想要在 Android 上有所突破,不過(guò)在今天之前得到的尺寸都不準(zhǔn)確。雖然很多人認(rèn)為沒(méi)必要這么較真,因?yàn)槊菜坪芏嗲闆r下用不到。不過(guò)我就當(dāng)這是一件很有挑戰(zhàn)性的事,一定要做到。對(duì),就是這么任性。

源碼中 android.view 包下的 Display 類提供了很多方法供程序員獲得顯示相關(guān)的信息,通過(guò)此類讓我們開啟了解設(shè)備屏幕之旅吧。

一、分辨率

需要注意的原來(lái)經(jīng)常使用的 getHeight() 與 getWidth() 已經(jīng)不推薦使用了,建議使用 getSize()來(lái)替代。

此方法原型如下:

    public void getSize(Point outSize) {  
        synchronized (this) {  
            updateDisplayInfoLocked();  
            mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);  
            outSize.x = mTempMetrics.widthPixels;  
            outSize.y = mTempMetrics.heightPixels;  
        }  
    }  

參數(shù)是一個(gè)返回參數(shù),用以返回分辨率的 Point,這個(gè) Point 也比較簡(jiǎn)單,我們只需要關(guān)注 x 和 y 這兩個(gè)成員就可以了。

用法如下:

    private void getDisplayInfomation() {  
        Point point = new Point();  
        getWindowManager().getDefaultDisplay().getSize (point);  
        Log.d(TAG,"the screen size is "+point.toString());  
    }  

結(jié)果如下:

    D/MainActivity﹕ the screen size is Point(800, 1280)  

此外 Display 又提供了一個(gè) getRealSize 方法,原型如下:

    public void getRealSize(Point outSize) {  
        synchronized (this) {  
            updateDisplayInfoLocked();  
            outSize.x = mDisplayInfo.logicalWidth;  
            outSize.y = mDisplayInfo.logicalHeight;  
        }  
    }  

從兩個(gè)方法的實(shí)現(xiàn)上看是有區(qū)別的,但是在通常情況下二者的返回值相同。那么差異究竟在哪里,下面做一些實(shí)驗(yàn)來(lái)驗(yàn)證一下。

首先,我將 Acitvity 設(shè)置不同的 theme,比如:

    android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"  
    android:theme="@android:style/Theme.NoTitleBar.Fullscreen"  

結(jié)果還是相同的。

接下來(lái)將我的 Activity 父類變成 ActionBarActivity,如下: public class MainActivity extends ActionBarActivity 期望 ActionBar 會(huì)占用一些屏幕,并在程序中動(dòng)態(tài)設(shè)置 Listview的Item 中的圖片大小。在機(jī)緣巧合之下,結(jié)果驗(yàn)證了在這種情況下,getSize 返回的結(jié)果變了。

代碼如下:

    private void getDisplayInfomation() {  
        Point point = new Point();  
        getWindowManager().getDefaultDisplay().getSize(point);  
        Log.d(TAG,"the screen size is "+point.toString());  
        getWindowManager().getDefaultDisplay().getRealSize(point);  
        Log.d(TAG,"the screen real size is "+point.toString());  
    }  

Log 如下:

    D/MainActivity﹕ the screen size is Point(800, 1202)  
    D/MainActivity﹕ the screen real size is Point(800, 1280)  

如果你不能夠輕易復(fù)現(xiàn)也不用急,保險(xiǎn)起見(jiàn),為了得到相對(duì)正確的信息還是使用 getRealSize() 吧。

二、屏幕尺寸

設(shè)備的物理屏幕尺寸。與幾年前不同,目前的手機(jī)屏幕已經(jīng)大到一只手握不下了。標(biāo)配早已經(jīng)到了5寸屏?xí)r代。

所謂屏幕尺寸指的是屏幕對(duì)角線的長(zhǎng)度,單位是英寸。

然而不同的屏幕尺寸是可以采用相同的分辨率的,而它們之間的區(qū)別在與密度(density)不同。

下面先介紹一下密度的概念,DPI、PPI,最后講解一下如何根據(jù)獲得的 Display 信息去求出屏幕尺寸。這是一個(gè)困擾我很久的問(wèn)題了。

三、屏幕密度

屏幕密度與 DPI 這個(gè)概念緊密相連,DPI 全拼是 dots-per-inch,即每英寸的點(diǎn)數(shù)。也就是說(shuō),密度越大,每英寸內(nèi)容納的點(diǎn)數(shù)就越多。 android.util 包下有個(gè) DisplayMetrics 類可以獲得密度相關(guān)的信息。

最重要的是 densityDpi 這個(gè)成員,它有如下幾個(gè)常用值:

    DENSITY_LOW = 120  
    DENSITY_MEDIUM = 160  //默認(rèn)值  
    DENSITY_TV = 213      //TV專用  
    DENSITY_HIGH = 240  
    DENSITY_XHIGH = 320  
    DENSITY_400 = 400  
    DENSITY_XXHIGH = 480  
    DENSITY_XXXHIGH = 640  

舉例如下:

    private void getDensity() {  
        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();  
        Log.d(TAG,"Density is "+displayMetrics.density+" densityDpi is "+displayMetrics.densityDpi+" height: "+displayMetrics.heightPixels+  
            " width: "+displayMetrics.widthPixels);  
    }  

Log 如下:

    the screen size is Point(1600, 2438)  
    the screen real size is Point(1600, 2560)  
    Density is 2.0 densityDpi is 320 height: 2438 width: 1600  

有了這些信息,我們是不是就可以計(jì)算屏幕尺寸了呢?

首先求得對(duì)角線長(zhǎng),單位為像素。

然后用其除以密度(densityDpi)就得出對(duì)角線的長(zhǎng)度了。

代碼如下:

    private void getScreenSizeOfDevice() {  
        DisplayMetrics dm = getResources().getDisplayMetrics();  
        int width=dm.widthPixels;  
        int height=dm.heightPixels;  
        double x = Math.pow(width,2);  
        double y = Math.pow(height,2);  
        double diagonal = Math.sqrt(x+y);  

        int dens=dm.densityDpi;  
        double screenInches = diagonal/(double)dens;  
        Log.d(TAG,"The screenInches "+screenInches);  
    }  

Log 如下:

    01-13 16:35:03.026  16601-16601/com.linc.listviewanimation D/MainActivity﹕ the screen size is Point(1600, 2438)  
    01-13 16:35:03.026  16601-16601/com.linc.listviewanimation D/MainActivity﹕ the screen real size is Point(1600, 2560)  
    01-13 16:35:03.026  16601-16601/com.linc.listviewanimation D/MainActivity﹕ Density is 2.0 densityDpi is 320 height: 2438 width: 1600 xdpi 338.666 ydpi 338.666  
    01-13 16:35:03.026  16601-16601/com.linc.listviewanimation D/MainActivity﹕ The screenInches 9.112922229586951  

如 Log 所見(jiàn),使用 heightPixels 得出的值是2483而不是正確的2560.從而使結(jié)果9.11反倒跟真實(shí)屏幕尺寸很接近。下面用正確的 height 再算一遍。

    01-13 16:39:05.476  17249-17249/com.linc.listviewanimation D/MainActivity﹕ the screen size is Point(1600, 2560)  
    01-13 16:39:05.476  17249-17249/com.linc.listviewanimation D/MainActivity﹕ the screen real size is Point(1600, 2560)  
    01-13 16:39:05.476  17249-17249/com.linc.listviewanimation D/MainActivity﹕ Density is 2.0 densityDpi is 320 height: 2560 width: 1600 xdpi 338.666 ydpi 338.666  
    01-13 16:39:05.476  17249-17249/com.linc.listviewanimation D/MainActivity﹕ The screenInches 9.433981132056605  

結(jié)果是9.43英寸,而真實(shí)值是8.91.如果再換一個(gè)設(shè)備,那么值差的更多。說(shuō)明上面的計(jì)算是錯(cuò)誤的。

那么錯(cuò)在哪里呢?densityDpi 是每英寸的點(diǎn)數(shù)(dots-per-inch)是打印機(jī)常用單位(因而也被稱為打印分辨率),而不是每英寸的像素?cái)?shù)。下面引出 PPI 這個(gè)概念。

四、PPI

Pixels per inch,這才是我要的每英寸的像素?cái)?shù)(也被稱為圖像的采樣率)。有了這個(gè)值,那么根據(jù)上面的公式就可以求導(dǎo)出屏幕的物理尺寸了。 還好 DisplayMetrics 有兩個(gè)成員是 xdpi 和 ydpi,對(duì)其描述是:

    /The exact physical pixels per inch of the screen in the X/Y dimension.  

屏幕 X/Y 軸上真正的物理 PPI。

Yes!Got it!

為了保證獲得正確的分辨率,我還是使用 getRealSize 去獲得屏幕寬和高像素。所以,經(jīng)過(guò)修改,代碼如下:

    private void getScreenSizeOfDevice2() {  
        Point point = new Point();  
        getWindowManager().getDefaultDisplay().getRealSize(point);  
        DisplayMetrics dm = getResources().getDisplayMetrics();  
        double x = Math.pow(point.x/ dm.xdpi, 2);  
        double y = Math.pow(point.y / dm.ydpi, 2);  
        double screenInches = Math.sqrt(x + y);  
        Log.d(TAG, "Screen inches : " + screenInches);  
    }  

Log is as follows:

    [plain] view plaincopy在CODE上查看代碼片派生到我的代碼片
    01-13 16:58:50.142  17249-17249/com.linc.listviewanimation D/MainActivity﹕ Screen inches : 8.914015757534717  

五、DIP

注意不要與上面的 DPI 混淆,這個(gè) DIP 是 Density Independent Pixel,直譯為密度無(wú)關(guān)的像素。

我們?cè)诓季治募惺褂玫?dp/dip 就是它。官方推薦使用 dp 是因?yàn)樗鼤?huì)根據(jù)你設(shè)備的密度算出對(duì)應(yīng)的像素。

公式為:pixel = dip*density

需要注意的是,我們?cè)?Java 代碼中對(duì)控件設(shè)置寬高是不可以設(shè)置單位的,而其自帶的單位是像素。所以如果動(dòng)態(tài)修改控件大小時(shí),我們的任務(wù)就來(lái)了,那就是將像素轉(zhuǎn)換為 dp。

實(shí)例代碼如下:

    //pixel = dip*density;  
    private int convertDpToPixel(int dp) {  
        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();  
        return (int)(dp*displayMetrics.density);  
    }  

    private int convertPixelToDp(int pixel) {  
        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();  
        return (int)(pixel/displayMetrics.density);  
    }   

參考:

http://stackoverflow.com/questions/19155559/how-to-get-android-device-screen-size