| 目錄(?)[+] |
大家?guī)兔D!
博主參加2014博客之星活動,大家?guī)兔ν镀崩玻?a rel="nofollow" >猛擊這里!
通過程序去了解硬件情況是一件十分有意思的事情。很早我就研究在 WM6.5上獲得屏幕物理尺寸,但一直沒有成功。后來又想要在 Android 上有所突破,不過在今天之前得到的尺寸都不準確。雖然很多人認為沒必要這么較真,因為貌似很多情況下用不到。不過我就當這是一件很有挑戰(zhàn)性的事,一定要做到。對,就是這么任性。
源碼中 android.view 包下的 Display 類提供了很多方法供程序員獲得顯示相關的信息,通過此類讓我們開啟了解設備屏幕之旅吧。
需要注意的原來經常使用的 getHeight() 與 getWidth() 已經不推薦使用了,建議使用 getSize()來替代。
此方法原型如下:
public void getSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);
outSize.x = mTempMetrics.widthPixels;
outSize.y = mTempMetrics.heightPixels;
}
}
參數是一個返回參數,用以返回分辨率的 Point,這個 Point 也比較簡單,我們只需要關注 x 和 y 這兩個成員就可以了。
用法如下:
private void getDisplayInfomation() {
Point point = new Point();
getWindowManager().getDefaultDisplay().getSize (point);
Log.d(TAG,"the screen size is "+point.toString());
}
結果如下:
D/MainActivity﹕ the screen size is Point(800, 1280)
此外 Display 又提供了一個 getRealSize 方法,原型如下:
public void getRealSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
}
}
從兩個方法的實現上看是有區(qū)別的,但是在通常情況下二者的返回值相同。那么差異究竟在哪里,下面做一些實驗來驗證一下。
首先,我將 Acitvity 設置不同的 theme,比如:
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
結果還是相同的。
接下來將我的 Activity 父類變成 ActionBarActivity,如下: public class MainActivity extends ActionBarActivity 期望 ActionBar 會占用一些屏幕,并在程序中動態(tài)設置 Listview的Item 中的圖片大小。在機緣巧合之下,結果驗證了在這種情況下,getSize 返回的結果變了。
代碼如下:
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)
如果你不能夠輕易復現也不用急,保險起見,為了得到相對正確的信息還是使用 getRealSize() 吧。
設備的物理屏幕尺寸。與幾年前不同,目前的手機屏幕已經大到一只手握不下了。標配早已經到了5寸屏時代。
所謂屏幕尺寸指的是屏幕對角線的長度,單位是英寸。
然而不同的屏幕尺寸是可以采用相同的分辨率的,而它們之間的區(qū)別在與密度(density)不同。
下面先介紹一下密度的概念,DPI、PPI,最后講解一下如何根據獲得的 Display 信息去求出屏幕尺寸。這是一個困擾我很久的問題了。
屏幕密度與 DPI 這個概念緊密相連,DPI 全拼是 dots-per-inch,即每英寸的點數。也就是說,密度越大,每英寸內容納的點數就越多。 android.util 包下有個 DisplayMetrics 類可以獲得密度相關的信息。
最重要的是 densityDpi 這個成員,它有如下幾個常用值:
DENSITY_LOW = 120
DENSITY_MEDIUM = 160 //默認值
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
有了這些信息,我們是不是就可以計算屏幕尺寸了呢?
首先求得對角線長,單位為像素。
然后用其除以密度(densityDpi)就得出對角線的長度了。
代碼如下:
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 所見,使用 heightPixels 得出的值是2483而不是正確的2560.從而使結果9.11反倒跟真實屏幕尺寸很接近。下面用正確的 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
結果是9.43英寸,而真實值是8.91.如果再換一個設備,那么值差的更多。說明上面的計算是錯誤的。
那么錯在哪里呢?densityDpi 是每英寸的點數(dots-per-inch)是打印機常用單位(因而也被稱為打印分辨率),而不是每英寸的像素數。下面引出 PPI 這個概念。
Pixels per inch,這才是我要的每英寸的像素數(也被稱為圖像的采樣率)。有了這個值,那么根據上面的公式就可以求導出屏幕的物理尺寸了。 還好 DisplayMetrics 有兩個成員是 xdpi 和 ydpi,對其描述是:
/The exact physical pixels per inch of the screen in the X/Y dimension.
屏幕 X/Y 軸上真正的物理 PPI。
Yes!Got it!
為了保證獲得正確的分辨率,我還是使用 getRealSize 去獲得屏幕寬和高像素。所以,經過修改,代碼如下:
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
注意不要與上面的 DPI 混淆,這個 DIP 是 Density Independent Pixel,直譯為密度無關的像素。
我們在布局文件中使用的 dp/dip 就是它。官方推薦使用 dp 是因為它會根據你設備的密度算出對應的像素。
公式為:pixel = dip*density
需要注意的是,我們在 Java 代碼中對控件設置寬高是不可以設置單位的,而其自帶的單位是像素。所以如果動態(tài)修改控件大小時,我們的任務就來了,那就是將像素轉換為 dp。
實例代碼如下:
//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