dpi ppi dip

通過程序去了解硬件情況是一件十分有意思的事情。很早我就研究在WM6.5上獲得屏幕物理尺寸,但一直沒有成功。後來又想要在Android上有所突破,不過在今天之前得到的尺寸都不準確。雖然很多人認爲沒必要這麼較真,因爲貌似很多情況下用不到。不過我就當這是一件很有挑戰性的事,一定要做到。對,就是這麼任性。


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

一、分辨率

需要注意的原來經常使用的getHeight()與getWidth()已經不推薦使用了,建議使用getSize()來替代。
此方法原型如下:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. public void getSize(Point outSize) {  
  2.     synchronized (this) {  
  3.         updateDisplayInfoLocked();  
  4.         mDisplayInfo.getAppMetrics(mTempMetrics, mDisplayAdjustments);  
  5.         outSize.x = mTempMetrics.widthPixels;  
  6.         outSize.y = mTempMetrics.heightPixels;  
  7.     }  
  8. }  
參數是一個返回參數,用以返回分辨率的Point,這個Point也比較簡單,我們只需要關注x和y這兩個成員就可以了。
用法如下:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. private void getDisplayInfomation() {  
  2.     Point point = new Point();  
  3.     getWindowManager().getDefaultDisplay().getSize(point);  
  4.     Log.d(TAG,"the screen size is "+point.toString());  
  5. }  
結果如下:
[plain] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. D/MainActivity﹕ the screen size is Point(800, 1280)  

此外Display又提供了一個getRealSize方法,原型如下:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. public void getRealSize(Point outSize) {  
  2.     synchronized (this) {  
  3.         updateDisplayInfoLocked();  
  4.         outSize.x = mDisplayInfo.logicalWidth;  
  5.         outSize.y = mDisplayInfo.logicalHeight;  
  6.     }  
  7. }  

從兩個方法的實現上看是有區別的,但是在通常情況下二者的返回值相同。那麼差異究竟在哪裏,下面做一些實驗來驗證一下。
首先,我將Acitvity設置不同的theme,比如:
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"  
  2. android:theme="@android:style/Theme.NoTitleBar.Fullscreen"  
結果還是相同的。

接下來將我的Activity父類變成ActionBarActivity,如下:
public class MainActivity extends ActionBarActivity
期望ActionBar會佔用一些屏幕,並在程序中動態設置Listview的Item中的圖片大小。在機緣巧合之下,
結果驗證了在這種情況下,getSize返回的結果變了。
代碼如下:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. private void getDisplayInfomation() {  
  2.     Point point = new Point();  
  3.     getWindowManager().getDefaultDisplay().getSize(point);  
  4.     Log.d(TAG,"the screen size is "+point.toString());  
  5.     getWindowManager().getDefaultDisplay().getRealSize(point);  
  6.     Log.d(TAG,"the screen real size is "+point.toString());  
  7. }  

Log如下:
[plain] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. D/MainActivity﹕ the screen size is Point(800, 1202)  
  2. D/MainActivity﹕ the screen real size is Point(800, 1280)  

如果你不能夠輕易復現也不用急,保險起見,爲了得到相對正確的信息還是使用getRealSize()吧。

二、屏幕尺寸

設備的物理屏幕尺寸。與幾年前不同,目前的手機屏幕已經大到一隻手握不下了。標配早已經到了5寸屏時代。
所謂屏幕尺寸指的是屏幕對角線的長度,單位是英寸。
然而不同的屏幕尺寸是可以採用相同的分辨率的,而它們之間的區別在與密度(density)不同。
下面先介紹一下密度的概念,DPI、PPI,最後講解一下如何根據獲得的Display信息去求出屏幕尺寸。這是一個困擾我很久的問題了。

三、屏幕密度

屏幕密度與DPI這個概念緊密相連,DPI全拼是dots-per-inch,即每英寸的點數。也就是說,密度越大,每英寸內容納的點數就越多。
android.util包下有個DisplayMetrics類可以獲得密度相關的信息。
最重要的是densityDpi這個成員,它有如下幾個常用值:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. DENSITY_LOW = 120  
  2. DENSITY_MEDIUM = 160  //默認值  
  3. DENSITY_TV = 213      //TV專用  
  4. DENSITY_HIGH = 240  
  5. DENSITY_XHIGH = 320  
  6. DENSITY_400 = 400  
  7. DENSITY_XXHIGH = 480  
  8. DENSITY_XXXHIGH = 640  

舉例如下:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. private void getDensity() {  
  2.     DisplayMetrics displayMetrics = getResources().getDisplayMetrics();  
  3.     Log.d(TAG,"Density is "+displayMetrics.density+" densityDpi is "+displayMetrics.densityDpi+" height: "+displayMetrics.heightPixels+  
  4.         " width: "+displayMetrics.widthPixels);  
  5. }  

Log如下:
[plain] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. the screen size is Point(1600, 2438)  
  2. the screen real size is Point(1600, 2560)  
  3. Density is 2.0 densityDpi is 320 height: 2438 width: 1600  

有了這些信息,我們是不是就可以計算屏幕尺寸了呢?
首先求得對角線長,單位爲像素。
然後用其除以密度(densityDpi)就得出對角線的長度了。
代碼如下:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. private void getScreenSizeOfDevice() {  
  2.     DisplayMetrics dm = getResources().getDisplayMetrics();  
  3.     int width=dm.widthPixels;  
  4.     int height=dm.heightPixels;  
  5.     double x = Math.pow(width,2);  
  6.     double y = Math.pow(height,2);  
  7.     double diagonal = Math.sqrt(x+y);  
  8.   
  9.     int dens=dm.densityDpi;  
  10.     double screenInches = diagonal/(double)dens;  
  11.     Log.d(TAG,"The screenInches "+screenInches);  
  12. }  

Log如下:

[plain] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 01-13 16:35:03.026  16601-16601/com.linc.listviewanimation D/MainActivity﹕ the screen size is Point(1600, 2438)  
  2. 01-13 16:35:03.026  16601-16601/com.linc.listviewanimation D/MainActivity﹕ the screen real size is Point(1600, 2560)  
  3. 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  
  4. 01-13 16:35:03.026  16601-16601/com.linc.listviewanimation D/MainActivity﹕ The screenInches 9.112922229586951  

如Log所見,使用heightPixels得出的值是2483而不是正確的2560.從而使結果9.11反倒跟真實屏幕尺寸很接近。下面用正確的height再算一遍。
[plain] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 01-13 16:39:05.476  17249-17249/com.linc.listviewanimation D/MainActivity﹕ the screen size is Point(1600, 2560)  
  2. 01-13 16:39:05.476  17249-17249/com.linc.listviewanimation D/MainActivity﹕ the screen real size is Point(1600, 2560)  
  3. 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  
  4. 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這個概念。

四、PPI

Pixels per inch,這纔是我要的每英寸的像素數(也被稱爲圖像的採樣率)。有了這個值,那麼根據上面的公式就可以求導出屏幕的物理尺寸了。
還好DisplayMetrics有兩個成員是xdpi和ydpi,對其描述是:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //The exact physical pixels per inch of the screen in the X/Y dimension.  
屏幕X/Y軸上真正的物理PPI。
Yes!Got it!
爲了保證獲得正確的分辨率,我還是使用getRealSize去獲得屏幕寬和高像素。所以,經過修改,代碼如下:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. private void getScreenSizeOfDevice2() {  
  2.     Point point = new Point();  
  3.     getWindowManager().getDefaultDisplay().getRealSize(point);  
  4.     DisplayMetrics dm = getResources().getDisplayMetrics();  
  5.     double x = Math.pow(point.x/ dm.xdpi, 2);  
  6.     double y = Math.pow(point.y / dm.ydpi, 2);  
  7.     double screenInches = Math.sqrt(x + y);  
  8.     Log.d(TAG, "Screen inches : " + screenInches);  
  9. }  
Log is as follows:
[plain] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. 01-13 16:58:50.142  17249-17249/com.linc.listviewanimation D/MainActivity﹕ Screen inches : 8.914015757534717  

五、DIP

注意不要與上面的DPI混淆,這個DIP是Density Independent Pixel,直譯爲密度無關的像素。
我們在佈局文件中使用的dp/dip就是它。官方推薦使用dp是因爲它會根據你設備的密度算出對應的像素。
公式爲:pixel = dip*density

需要注意的是,我們在Java代碼中對控件設置寬高是不可以設置單位的,而其自帶的單位是像素。所以如果動態修改控件大小時,
我們的任務就來了,那就是將像素轉換爲dp。
實例代碼如下:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //pixel = dip*density;  
  2. private int convertDpToPixel(int dp) {  
  3.     DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();  
  4.     return (int)(dp*displayMetrics.density);  
  5. }  
  6.   
  7. private int convertPixelToDp(int pixel) {  
  8.     DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();  
  9.     return (int)(pixel/displayMetrics.density);  
  10. }   

參考:

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



dpi和ppi區別

dpi爲dots per inch的縮寫,中譯爲每英吋點數,指影像擷取工具或顯示工具每單位長度(英吋)所能擷取或顯示數據的能力。因此點數越多,輸出的圖案越細緻,印刷輸出則須300dpi或更高。

ppi則是pixel per inch的縮寫,主要是論及光學分辨率,因爲感光組件上纔有像素(pixel)。


測試:小米3輸出數據


public class MainActivity extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DisplayMetrics metrics = getResources().getDisplayMetrics();
double testdpi = Math.hypot(1080, 1920)/5;
Log.i("DisplayMetrics", "metrics.density  " + metrics.density
+ "  metrics.densityDpi  " + metrics.densityDpi+"  testdpi  "+testdpi);
}


}


01-19 12:08:40.897: I/DisplayMetrics(2969): metrics.density  3.0  metrics.densityDpi  480
01-19 12:11:37.967: I/DisplayMetrics(3319): metrics.density  3.0  metrics.densityDpi  480  testdpi  440.58143401645964
01-19 12:14:06.807: I/DisplayMetrics(3659): metrics.density  3.0  metrics.densityDpi  480  testdpi  4.917646181702473

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章