Android 關(guān)于"尺寸"的那些事(dp,dip,sp,pt,px...)

必備概念

屏幕大?。浩聊淮笮∈鞘謾C(jī)對(duì)角線的物理尺寸,以英寸inch為單位。比如我的Mix 2手機(jī)屏幕大小為5.99 inches,意味著我的屏幕對(duì)角線長度為5.99inches = 5.99 * 2.54 = 15.2146cm

分辨率:屏幕的像素點(diǎn)數(shù),一般表示為a*b。例如某手機(jī)分辨率為21601080,意味著手機(jī)屏幕的豎直方向(長)有2160個(gè)像素點(diǎn),水平方向(寬)有1080個(gè)像素點(diǎn)。

單位定義

px:Pixels ,像素;對(duì)應(yīng)屏幕上的實(shí)際像素,是畫面中最小的點(diǎn)(單位色塊),像素大小沒有固定長度值,不同設(shè)備上1個(gè)單位像素色塊大小不同。

這么說可能有點(diǎn)陌生,用屏幕分辨率來說,今年流行起來的“全面屏”分辨率是 2160*1080,但是你也可以發(fā)現(xiàn),雖然很多全面屏手機(jī)分辨率一樣,但是明顯看得出來屏幕大小不一樣,這也解釋了“不同設(shè)備像素色塊大小是不同的”。

pt:1pt=1/72 inch,用于印刷業(yè),非常簡單易用;

dpi:Dots Per Inch,每英寸點(diǎn)數(shù);詳見ppi

ppi:Pixels Per Inch,每英寸像素?cái)?shù);數(shù)值越大顯示越細(xì)膩。計(jì)算式:ppi = 屏幕對(duì)角線像素?cái)?shù) / 屏幕對(duì)角線長度。

還是舉全面屏的例子,分辨率2160*1080,屏幕大小是5.9inches,勾股定理可以得到對(duì)角線像素?cái)?shù)大約是2415,那么ppi = 2415 / 5.99 = 403.

事實(shí)上dpi 和 ppi 一定程度上可以劃等號(hào),都表示像素密度,計(jì)算方式完全一致,只不過使用場(chǎng)景不一樣。dpi中的dots點(diǎn)屬于打印或印刷等領(lǐng)域,例如drawable 文件對(duì)應(yīng)的就是dpi,而ppi中的pixel屬于屏幕顯示等領(lǐng)域

dp/dip : Density-independent Pixels,密度無關(guān)像素 - 基于屏幕物理密度的抽象單位。1dp等于 160 dpi 屏幕上的dpx,這是 系統(tǒng)為“中”密度屏幕假設(shè)的基線密度。在運(yùn)行時(shí),系統(tǒng) 根據(jù)使用中屏幕的實(shí)際密度按需要以透明方式處理 dp 單位的任何縮放 。dp 單位轉(zhuǎn)換為屏幕像素很簡單:px = dp * (dpi / 160)。 例如,在 240 dpi 屏幕上,1 dp 等于 1.5 物理像素。在定義應(yīng)用的 UI 時(shí)應(yīng)始終使用 dp 單位 ,以確保在不同密度的屏幕上正常顯示 UI。

如果看完文章還是覺得很懵,那么可以直接記?。?strong>1dp單位在設(shè)備屏幕上總是等于1/160 inch。

sp:Scale-independent Pixels ,與dp單位相似,也會(huì)根據(jù)用戶的字體大小偏好進(jìn)行縮放。

深入理解

首先我們放上源碼中對(duì)尺寸單位的轉(zhuǎn)換

public static float applyDimension(int unit, float value,
                                   DisplayMetrics metrics)
{
    switch (unit) {
    case COMPLEX_UNIT_PX:
        return value;
    case COMPLEX_UNIT_DIP:
        return value * metrics.density;
    case COMPLEX_UNIT_SP:
        return value * metrics.scaledDensity;
    case COMPLEX_UNIT_PT:
        return value * metrics.xdpi * (1.0f/72);
    case COMPLEX_UNIT_IN:
        return value * metrics.xdpi;
    case COMPLEX_UNIT_MM:
        return value * metrics.xdpi * (1.0f/25.4f);
    }
    return 0;
}

可以看到,輸入值類型為dp時(shí),返回 value * DisplayMetrics.density,到這里我們可能會(huì)發(fā)懵:嗯?不對(duì)啊,前面我們不是通過px 和 dp 的換算公式來計(jì)算的么,怎么這里就簡簡單單乘了一個(gè)DisplayMetrics.density?不要慌,我們先看看源碼中對(duì)DisplayMetrics.density的介紹。

/**
 * The logical density of the display.  This is a scaling factor for the
 * Density Independent Pixel unit, where one DIP is one pixel on an
 * approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), 
 * providing the baseline of the system's display. Thus on a 160dpi screen 
 * this density value will be 1; on a 120 dpi screen it would be .75; etc.
 *  
 * <p>This value does not exactly follow the real screen size (as given by 
 * {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of
 * the overall UI in steps based on gross changes in the display dpi.  For 
 * example, a 240x320 screen will have a density of 1 even if its width is 
 * 1.8", 1.3", etc. However, if the screen resolution is increased to 
 * 320x480 but the screen size remained 1.5"x2" then the density would be 
 * increased (probably to 1.5).
 *
 * @see #DENSITY_DEFAULT
 */
public float density;

源碼注釋中說到“在160dpi的屏幕下,density的值為1,而在120dpi的屏幕下,density的值為0.75”,我們可以大膽的猜測(cè)一下,120dpi下的density=0.75的原因是120dpi * 1 /160dpi=0.75。實(shí)際上,也就是這么回事。我們下面會(huì)仔細(xì)的分析。

需要補(bǔ)充一下,通常意義上Android 屏幕的密度,指的是像素密度dpi/ppi,對(duì)應(yīng)于源碼中的DisplayMetrics.densityDpi。

為什么引入dp?

Android 引入了dp這一單位,使得不論多大屏幕,多大dpi,顯示的效果始終保持一致。

但是根據(jù)前面我們提到的px與dp的換算公式px = dp * (dpi / 160),很顯然,由于相同分辨率但不同屏幕大小的設(shè)備dpi是不同的,導(dǎo)致px和dp的基本不存在一個(gè)固定的換算關(guān)系,為了方便屏幕適配,Android設(shè)置了6個(gè)通用的密度,換算px與dp時(shí)采取通用密度計(jì)算,而非設(shè)備實(shí)際的密度。

以下為6種通用密度,以及其最小的分辨率

  • ldpi(低)~120dpi (240*320)
  • mdpi(中)~160dpi(320*480) (基準(zhǔn))
  • hdpi(高)~240dpi(480*800)
  • xhdpi 720P (超高)~320dpi(720*1280)
  • xxhdpi 1080p(超超高)~480dpi(1080*1920)
  • xxxhdpi 4k(超超超高)~640dpi(2160*3840)

得到上面通用密度之后,我們換算dp與px多了一種簡便方式。前面我們提到Android將mdpi作為基準(zhǔn),此時(shí)1px = 1dp,又有px = dp * (dpi / 160),所以我們可以很容易的得到以下?lián)Q算:

  • ldpi:1dp = 0.75px,density = 0.75
  • mdpi:1dp = 1px,density = 1
  • hdpi:1dp = 1.5px,density = 1.5
  • xhdpi:1dp = 2px,density = 2
  • xxhdpi:1dp = 3px,density = 3
  • xxxhdpi:1dp = 4px,density = 4

還記不記得前面源碼中的density屬性,實(shí)際上DisplayMetrics.density = dpi / 160 ,表示的就是在某個(gè)通用密度下dp與px的換算比(1dp/1px的值)

補(bǔ)充

關(guān)于作圖

這部分其實(shí)和程序員自身已經(jīng)關(guān)系不大了,畢竟參與工作之后這些都是UI人員的活兒了。不過鑒于現(xiàn)在我還只是一枚在校生,還是記下來以免自己遺漏吧。

建議在xhdpi中作圖

原因嘛,首先現(xiàn)在主流分辨率是1080p,以及最近流行起來的全面屏18:9,而xhdpi對(duì)應(yīng)720p,向低dpi兼容自然沒問題,即便在xxhdpi中顯示,也會(huì)有個(gè)不錯(cuò)的效果。而如果以1920*1080作圖,顯然圖片素材占用的內(nèi)存很大,而且也會(huì)增大應(yīng)用安裝包的大小。

資源文件夾

只有一個(gè)原則:資源放入對(duì)應(yīng)dpi的文件夾中,Android會(huì)機(jī)智的加載合適的資源。

以drawable資源為例:

  • ldpi ~ drawable-ldpi
  • mdpi ~ drawable-mdpi
  • hdpi ~ drawable-hdpi
  • xhdpi ~ drawable-xhdpi
  • xxhdpi ~ drawable-xxhdpi
  • xxxhdpi ~ drawable-xxxhdpi

我們平時(shí)開發(fā)小項(xiàng)目&對(duì)UI要求不高時(shí),只使用一套xhdpi的資源就足夠了,雖然這可能會(huì)導(dǎo)致在hdpi及以下的手機(jī)中有些卡頓,因?yàn)閤hdpi的圖片運(yùn)行在hdpi及以下的手機(jī)上會(huì)比較吃內(nèi)存,不過無傷大雅。

而如果不為圖片資源犯愁時(shí)(有UI人員的支持,就是任性),就可以添加所有dpi的資源。當(dāng)然,重點(diǎn)還是要滿足ldpi:mdpi:hdpi:xhdpi:xxhdpi=3:4:6:8:12的規(guī)律。

好像說了不少廢話,哈哈,大概就這么多吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容