dp 這個單位是安卓發明的么?為什麼 px=dp*(dpi/160)?

在Xdpi下繪製Xpx長度,實際的物理距離都是1英寸,為什麼一定要選160呢?

如果設定寬高為厘米,或者英寸之類的物理單位,屏幕根據dpi的大小來計算要顯示多少像素才能達到這個物理單位,不是更好?為什麼要引入dp?


作為一個經常拿著計算器算長寬,然後給 Android 應用各個解析度提供界面切圖的死美工,我想嘗試回答一下後半個問題,也就是為啥以 160 dpi 作為基準:

Android Design [1] 里把主流設備的 dpi 歸成了四個檔次,120 dpi、160 dpi、240 dpi、320 dpi

實際開發當中,我們經常需要對這幾個尺寸進行相互轉換(比如先在某個解析度下完成設計,然後縮放到其他尺寸微調後輸出),一般按照 dpi 之間的比例即 2:1.5:1:0.75 來給界面中的元素來進行尺寸定義。

也就是說如果以 160 dpi 作為基準的話,只要尺寸的 DP 是 4 的倍數,XHDPI 下乘以 2,HDPI 下乘以 1.5,LDPI 下乘以 0.75 即可滿足所有尺寸下都是整數 pixel 。

但假設以 240 dpi 作為標準,那需要 DP 是 3 的倍數,XHDPI 下乘以 1.333,MDPI 下乘以 0.666 ,LDPI 下除以 2

而以 LDPI 和 XHDPI 為基準就更複雜了,所以選擇 160 dpi

話說我覺得在給 Android 的應用提供切圖的時候經常像是在算 24 點。。。

[1] http://developer.android.com/design/style/devices-displays.html


1.在Xdpi下繪製Xpx長度,實際的物理距離都是1英寸,為什麼一定要選160呢?

答:這個在Google的官方文檔中有給出了解釋,因為第一款Android設備(HTC的T-Mobile G1)是屬於160dpi的。

The generalized sizes and densities are arranged around a baseline configuration that is a normal size and mdpi(medium) density. This baseline is based upon the screen configuration for the first Android-powered device, the T-Mobile G1, which has an HVGA screen (until Android 1.6, this was the only screen configuration that Android supported).

為什麼說是屬於呢?

因為我查過T-Mobile G1的DPI,其實它準確的DPI不等於160,G1的配置信息如下:

  • 屏幕尺寸:3.2 寸(8.1 厘米)
  • 解析度:320 x 480(HVGA)

在計算DPI前我先簡單介紹一下DPI以及相關概念,DPI(Dots Per Inch)翻譯為每英寸像素點的個數,英寸是一個物理單位,1英寸 = 2.54厘米,大家平常說的手機屏幕4.3英寸,4.5英寸指的是屏幕對角線的長度,如下圖所示:

解析度480 x 800,屏幕尺寸4.3英寸和解析度540 x 960,屏幕尺寸4.5英寸的DPI分別是:

如果按照這樣的計算方式的話,T-Mobile G1應該為180dpi,這個計算大家可以使用計算器或者手動的計算方式得出,為了更具說服力,我貼出自己的計算截圖(使用DPI Caculator計算)

大家可以看到計算出來的結果是180而不是160,大家會想那為什麼不直接用180作為基準(mdpi)而是160呢?這就好像為什麼是二進位而不是其他進位,就像@JJ Ying說的,180上下不好做適配,但是160無論是乘以0.5/2/1.5都很好適配,這就是為什麼我說屬於而不是等於,Android其實為了不至於為每一個設備製造商做適配(其實資源文件的分包就算適配了:drawable-hdpi,drawable-ldpi),將不同屏幕大小和不同dpi的設備大致劃分為四類,如下圖:

大家可以看到T-Mobile G1的參數屬於mdpi區域的,以上就是取160dpi作為基準的原因。

2.如果設定寬高為厘米,或者英寸之類的物理單位,屏幕根據dpi的大小來計算要顯示多少像素才能達到這個物理單位,不是更好?為什麼要引入dp?

如果有興趣可以看一下這個類的源碼(網址):GrepCode: android.util.DisplayMetrics (.java) ,這個類中有很詳細的dpi相關的成員函數和變數,下面的代碼是在開發時獲取dpi的代碼,

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
iDensity = (int)( metrics.density * 160 );

(1)我先回答前半部分,「如果設定寬高為厘米,或者英寸之類的物理單位,屏幕根據dpi的大小來計算要顯示多少像素才能達到這個物理單位,不是更好?」

答:我認為設置寬高為厘米/英寸,然後先通過上面代碼動態計算dpi的值,然後再根據計算出的dpi計算出顯示控制項需要的寬和高是可行的,但是絕對不是更好,而是非常差,因為開發的原則遵循的是界面設計和功能邏輯分離,在程序中每次都首先需要計算一下dpi才能設定其他控制項的寬高屬性是很不友好的,你想想,你啟動了某個程序,它界面一直出不來,後台還在計算著DPI,用戶體驗也不會好,超過5s估計就被當作ANR給kill掉了。

(2)為什麼要引入dp?

答:dp實際是dip(density independent pixel),獨立密度像素,意思就是與density密度(dpi)無關,我使用dp作為單位設置控制項,不管你什麼屏幕大小,多大的dpi,顯示的效果始終保持一致。假如我們不引入dp,還是使用原始的px,現在我們需要在手機屏幕上繪製一條直線,在160dpi(每英寸160個像素點),而寬度是1英寸的手機上做開發和測試工作,我們設置這條直線長度是160px(佔據160個像素點),也就是直線長度正好是手機的寬度,開發工作完成,我們把app發布到市場上,這時候JJ Ying用他同樣寬度1英寸,但是240dpi的手機安裝了這個app,他能不抓狂!本該長度有屏幕這麼寬的線視覺上只有原來的2/3,如果整個UI都使用px作為單位,就會有如下的效果。

Example application without support for different densities, as shown on low, medium, and high density screens.

上面這張圖來自Google的開發網站。如果引入dp這個單位,這種問題就不會發生了,還是以上面的例子說明,如果在160dpi的設備上使用的是160dp,無論移植到240,120dpi上顯示效果都是一致的。px和dp的換算公式px = dp * (dpi / 160),效果如下圖:


1 是不是Android 發明的DP,這個我不知道,參考別人的答案吧。(是安卓定義了這個單位,但這類概念相信早就有了)

2 為什麼選160,因為第一款機子是160,於是就成為標準了。(160 約等於HVGA 屏幕手機的像素密度)

3 為什麼要用DP,簡單來說減少開發成本。

一個app要在240x320 解析度的低端機上運行,也要在1080P 的高端機上運行,軟體寫代碼的時候,這個圖標定義多大呢。用px做單位顯然不合適了,所以引入了dp。不同開發多個版本而實現UI的自適應。

系統只認px,不認識厘米,設定160dp的長度,相當於告訴手機在160dpi屏幕上顯示160px,在240dpi屏幕上顯示240px 。如果根據屏幕密度換算像素讓系統去用,你這台手機是方便了,"在Xdpi下繪製Xpx長度,實際的物理距離都是1英寸" 屏幕升級了呢?代碼重寫一遍再調一次UI?

話說iOS就是為了省事,iPHone4 直接翻倍。原來iPhone iPhone 3G iPhone 3GS 都是160,突然iPhone4 就320了。為的是開發者方便,UI大小相關的乘以2即可,GUI設計切圖無腦放大一倍,@JJ Ying 同學也不同按計算器算24了。但是翻倍一時爽,看著Android陣營屏幕穩步升級,iPhone總不能再翻倍吧,320已經視網膜了。所以iPhone 5 只好加長了,被逼無奈。@JJ Ying 同學還是可以按照iPhone 4 的規格切圖,軟體只要針對iPhone 5 調整下坐標位置就好了。

然後我為iPhone 6 捉急啊,如果要上1080P,就沒有省事的方案了,@JJ Ying 同學又得拿出計算器切圖了 XD


現有回答已經講過幾個原因了:

1. 160dpi 為基準比較方便換算。

但是仔細想的話,這個理由其實不成立。因為其他幾個dpi本來就是根據基準定出來的。比如用240dpi作為基準,你可以改為360dpi(1.5)、240dpi、180dpi(0.75)、120dpi(0.5)。

2. 第一款機型是160dpi。

這看上去是比較靠譜的原因。但其實如@鄭旺所說,第一款機型G1實際上是180dpi的。

3. 我個人補充一個可能:

dp實際上幾乎就是css px。

現在css pixel的定義是1/96in。但是注意css pixel其實原本是相對單位,以視角定義。即使現在固定為1/96in,但是所有絕對長度單位都是可根據情況進行anchor,幾乎所有屏幕設備都是根據設備像素進行比例縮放,也就是跟dp是完全類似的。而css pixel的1/96in是按照桌面屏幕來算的,移動設備的典型可視距離比桌面屏幕要近,如果取0.6倍,則換算結果正好是1/160in。

當然這同樣不足以解釋為什麼是160dpi而不是180dpi。我個人的猜測是因為iphone是160dpi的。


經過一番搜尋後,我自我解答

dp就是一個類似於pt的單位

pt全稱為point,但中文不叫「點」,查英語字典可以看到,確切的說法是一個專用的印刷單位「磅」,大小為1/72英寸。所以它是一個自然界標準的長度單位,也稱為「絕對長度」。  1in = 2.54cm = 25.4 mm = 72pt = 6pc  因此就有這樣的說法,在網頁設計中,pixel是相對大小,而point是絕對大小。

在安卓設計中,pixel是相對大小,而dp是絕對大小。之所以是160來換算,仍然不太懂



上面的公式講得也太複雜了點,平時都使用自動計算方式

順便附上鏈接DPI Calculator


上面的回答得都很詳細了 我來總結一些經驗:

1.xml布局時候盡量少出現dp 杜絕px

2.多用weight權重分割布局 gravity設置重心布局

3.align+(如layout_alignright)系列布局再用margin+(如layout_marginleft)系列調整view之間的距離(我認為align和margin組合對於網傳那麼嚴重的碎片化可以很好解決 現在低端機都能350dp以上 dp跨度以後會越來越小 碎片化遲早會完美解決的。所以碎片化明顯是誇大的) 能最大限度的減少不同dip手機的布局誤差

4.一些按鈕,拖動條肯定會因為機型繁多dp各異發生大小變化 為了適應低端機 應該適當做小點

我認為安卓機採取iPhone6 plus的dp再高百分之十 屏幕就已經完美了 此時色彩明艷感達到最好(三星的屏做到了)

我所指的碎片化是指應用軟體開發層面上的。 關於系統和驅動程序的碎片化不作考慮 我估計很難解決

有更好的解決方式 請大神留下評論。


說那麼多沒用,簡單說一句,dp就是一個新的長度單位也就是跟inch(英寸),m,mm,cm類似的單位,只不過1dp的長度為(1inch/160),比如你要畫一個寬度為 2dp 的線(即2/160(1/80)inch),也就是讓它在所有的屏幕上都被人覺得是1/80inch寬度的線。如此簡單的問題為何需要各位大咖refer to xx,鄙人實在不理解;至於你的第二個問題,為什麼需要除以160,那就跟1 l-y (光年)= 946073047258080000cm,你為什麼不說pencil長為(1.0570008340246154637094605244851e-17) l-y ,而要說pencil長為10cm一樣了


PM關於設計那些事兒(一)

是安卓發明的


好像看了半天也沒看出個唯一答案來


我在想會不會存在dpi相同尺寸不同的手機,也就是同樣的密度,不同大小。這app跑起來就蛋疼了。哇塞,怪不得手機版、pad版,這倆貨不就是這情況嗎

好吧 http://stackoverflow.com/questions/2638202/android-multiple-screen-sizes-with-same-density


其實應該是dp = px * (dpi/160)


只是想設計個APP界面與切圖而也,搞這麼複雜就行了;直接說160是比較適中的一種解析度容;在做設計的時候以480*800為適中然後向上剩2或向下除2或1.5; @小東別院 最後輸入四種不同的尺寸就得了;

而且上面的公式講得也太複雜了點有沒得更好更簡單的計算公式;


推薦閱讀:

你見過哪些令你瞠目結舌的 Objective-C 代碼技巧?
APICloud是免費用的嗎?
android v7包里的Toolbar,怎麼定製圖標、字體居中的效果?

TAG:產品設計 | 解析度 | Android開發 | 用戶界面設計 | 移動開發 |