Dimension就是Android里的尺度標(biāo)準(zhǔn)了误债,其實(shí)呢,世界上本來(lái)有很多度量衡的妄迁,比如說(shuō)我們常用的公制度量衡寝蹈,還有英制度量衡,可能還有等等登淘。箫老。。
Android機(jī)型眾多黔州,分辨率耍鬓,尺寸阔籽,形狀(沒(méi)錯(cuò),Android系統(tǒng)還考慮過(guò)非規(guī)則顯示屏)各異牲蜀,這對(duì)于Android系統(tǒng)設(shè)計(jì)而言是一個(gè)挑戰(zhàn)笆制。對(duì)于一個(gè)實(shí)際的應(yīng)用程序來(lái)說(shuō),怎么知道具體的設(shè)備多長(zhǎng)多粗(*/ω\*)呢涣达。
確實(shí)呢在辆,這不太好解決哦。然而峭判,Google還是想到了一個(gè)辦法开缎。
Android面向應(yīng)用開(kāi)發(fā)呢,提出了一個(gè)叫做dip的長(zhǎng)度單位林螃;
而Android面向設(shè)備呢奕删,要求它聲明自己的xdpi和ydpi(注意,并不是dpi)疗认。
廢話不多說(shuō)完残,直接切入Android代碼了解下Dimension的那些事兒。主要的兩個(gè)嘉賓是來(lái)自android.util的TypedValue和DisplayMetrics横漏。這兩個(gè)類比較簡(jiǎn)單谨设,加起來(lái)代碼也才800+行,中間很大篇幅還是注釋缎浇。
TypedValue
TypedValue算是Android自己的用于Dimension換算的工具類扎拣,它有很多方法,其中的complexToDimensionPixelSize(int data, DisplayMetrics metrics)
甚至在View的構(gòu)造方法中用于解析xml中的長(zhǎng)度值素跺,可見(jiàn)TypedValue的可靠程度二蓝。然而,我們把注意力放在它的另一個(gè)方法applyDimension(int unit, float value, DisplayMetrics metrics)
指厌,這個(gè)方法的作用是把單位為px刊愚,dp,sp踩验,pt(什么鬼鸥诽?),in(什么鬼箕憾?)牡借,mm(什么鬼?)的長(zhǎng)度換算到以px為單位的長(zhǎng)度袭异,全部代碼如下:
/**
* Converts an unpacked complex data value holding a dimension to its final floating
* point value. The two parameters <var>unit</var> and <var>value</var>
* are as in {@link #TYPE_DIMENSION}.
*
* @param unit The unit to convert from.
* @param value The value to apply the unit to.
* @param metrics Current display metrics to use in the conversion --
* supplies display density and scaling information.
*
* @return The complex floating point value multiplied by the appropriate
* metrics depending on its unit.
*/
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;
}
這段代碼的可讀性已經(jīng)簡(jiǎn)單到了相當(dāng)?shù)某潭饶屏叶疾缓靡馑级嗫匆谎?///▽///)。然而,這確確實(shí)實(shí)是Android自己的代碼俊鱼,這時(shí)候,我又審視了一遍自己的態(tài)度畅买,并不是越花哨的代碼越好并闲,而是越好的代碼越“花哨”(看人家的注釋,眉飛色舞9刃摺)帝火。
這個(gè)方法推薦大家反復(fù)使用,因?yàn)橥瑯拥姆椒ㄎ覀冏约翰荒軐?xiě)得更簡(jiǎn)潔湃缎、更好犀填。
這個(gè)方法中最有內(nèi)涵的莫過(guò)于這一部分了啊:
好的嗓违,到了這里九巡,除了pt,in蹂季,mm不明所以外差不多都懂了啊冕广,而上圖又都指向了另一個(gè)類DisplayMetrics。先草草地中斷下TypedValue的了解偿洁,看看DisplayMetrics是個(gè)什么鬼撒汉。
DisplayMetrics
關(guān)于DisplayMetrics呢,有人說(shuō)用了很多次了涕滋,可謂是花式編程不勝枚舉睬辐。然而,會(huì)用就算了解了嗎宾肺?(OS[注]:廢話溯饵,都會(huì)用了,還有啥好了解的)
筆者個(gè)人認(rèn)為爱榕,對(duì)于DisplayMetrics瓣喊,我們了解的再多可能也不見(jiàn)得夠啊。
剛才在TypedValue當(dāng)中黔酥,我們見(jiàn)識(shí)了DisplayMetrics的三個(gè)成員藻三,分別是density,scaledDensity和xdpi跪者。頭兩個(gè)大家可能也認(rèn)識(shí)棵帽,最后一個(gè)就蒙嗶了對(duì)不對(duì),啊~~
我們先簡(jiǎn)單的看一下渣玲,這幾個(gè)成員的代碼:
/**
* 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;
/**
* A scaling factor for fonts displayed on the display. This is the same
* as {@link #density}, except that it may be adjusted in smaller
* increments at runtime based on a user preference for the font size.
*/
public float scaledDensity;
/**
* The exact physical pixels per inch of the screen in the X dimension.
*/
public float xdpi;
看完這仨的注釋逗概,我就奔潰了,真?越短越難啊忘衍。什么鬼逾苫?關(guān)鍵是代碼里怎么還有個(gè)ydpi:
/**
* The exact physical pixels per inch of the screen in the Y dimension.
*/
public float ydpi;
這些東西究竟是干嘛的卿城?猴子請(qǐng)來(lái)的逗?jiǎn)裘矗?/p>
我抄起手邊的一臺(tái)手機(jī),洋洋灑灑碼了幾行代碼铅搓,讀了讀這幾個(gè)量瑟押,結(jié)果如下:
圖中的density dpi是我看見(jiàn)DisplayMetrics里的成員densityDpi時(shí),不自禁打粗來(lái)的:
/**
* The screen density expressed as dots-per-inch. May be either
* {@link #DENSITY_LOW}, {@link #DENSITY_MEDIUM}, or {@link #DENSITY_HIGH}.
*/
public int densityDpi;
反正都不懂星掰,不在乎多一個(gè)多望。
好了,到這里氢烘,我陷入了深深地思考“為什么我要了解這么深怀偷,這不是有病嗎?”然而答案是否定的播玖。
經(jīng)過(guò)了艱苦卓絕的努力和試驗(yàn)和思考后椎工,我得出了一些結(jié)論:
The End
人家說(shuō)結(jié)束不過(guò)是另一個(gè)開(kāi)始而已。嗯黎棠,確實(shí)呢晋渺,能看到這里首先謝謝各位的耐心。然而脓斩,一個(gè)好(壞木西?)消息是,我們終于要開(kāi)始(手動(dòng)斜眼)介紹Android應(yīng)對(duì)眾多形態(tài)各異的設(shè)備的手段了随静。八千。。
剛才的測(cè)試中燎猛,density和scaledDensity只是簡(jiǎn)單的dp恋捆,sp到px的換算比例而已,這是誰(shuí)都知道的東西重绷。它們都是3.0沸停,說(shuō)明1dp==3px,1sp==3px昭卓。但是愤钾,這個(gè)比例值3.0是如何決定的呢?嗯候醒,這就要看density的注釋中的那段話了能颁,它說(shuō)“如果densityDpi的值是160dpi的若干倍,那么density就是這個(gè)倍數(shù)了”(筆者英文不好倒淫,譯得比較糙伙菊,不服你打我啊ヽ(`⌒′)ノ)。
然而,density注釋里又有一些囈語(yǔ)“一個(gè)240*320的屏幕即便1.8寸或1.3寸寬镜硕,density也可以是1”运翼。這是怎么個(gè)情況呢?我一開(kāi)始也暈兴枯,后來(lái)發(fā)現(xiàn)了蹊蹺(此處陰險(xiǎn)表情)南蹂。請(qǐng)大家注意下咯,我文中測(cè)試的手機(jī)顯示densityDpi是480念恍,而我其實(shí)也測(cè)了另外一臺(tái)手機(jī)(此處陰險(xiǎn)表情again):
這臺(tái)手機(jī)呢,也是480的densityDpi晚顷。然而峰伙,事實(shí)上,雖然兩臺(tái)手機(jī)的分辨率是一樣的(1920*1080)该默,尺寸卻分別是5.0寸和5.2寸瞳氓。到這里我想起了那句囈語(yǔ),又于是聯(lián)想到這么幾點(diǎn):
- 市場(chǎng)上確實(shí)存在分辨率相同但是尺寸有差異的手機(jī)栓袖,這些手機(jī)的densityDpi和density又恰巧一致匣摘;
- 對(duì)于程序猿而言,我們用到的density裹刮,scaledDensity和densityDpi很多音榜,用到的xdpi,ydpi很少捧弃;
- xdpi赠叼,ydpi的注釋表明它們才是實(shí)際的每英寸像素點(diǎn)數(shù)(這也是為啥兩個(gè)手機(jī)的xdpi,ydpi會(huì)有明顯差異的原因)违霞。
綜上嘴办,Google的意圖大概可以這么解釋:
- Google希望Android表達(dá)相同的內(nèi)容在5.0寸和5.2寸的手機(jī)上所呈現(xiàn)的比例是接近的,而又不能增加程序猿的工作量买鸽,那么涧郊,就呈現(xiàn)相同的dpi而程序猿看好了,這就是為什么densityDpi相同的原因眼五,因?yàn)樽彼遥琩ensityDpi相同就基本保證了內(nèi)容顯示比例的相同。
- 實(shí)際上的手機(jī)尺寸必然有差異弹砚,對(duì)于差異小的手機(jī)双仍,統(tǒng)一到同一densityDpi,差異大的話桌吃,就可以區(qū)分開(kāi)比如xxhdpi或hdpi的密度設(shè)定了(上述的兩個(gè)手機(jī)的xdpi朱沃,ydpi都是比較高的,歸到xhdpi(320dpi)不如歸到xxhdpi(480dpi),所以densityDpi都顯示為480了)逗物。
- 如果一定要顯示特定的物理長(zhǎng)度的話搬卒,那么,density顯然是做不到的翎卓,這時(shí)候xdpi和ydpi就能起到作用了契邀。這也是為什么xdpi可以計(jì)算出in(英寸),mm(毫米)和pt(point失暴,這是一個(gè)概念坯门,這個(gè)點(diǎn)的尺寸在不同手機(jī)是等長(zhǎng)的)。
可以想到逗扒,運(yùn)行在有相同密度的不同尺寸的手機(jī)上時(shí)古戴,Android面向程序猿是無(wú)差別的dp長(zhǎng)度,程序猿可以肆意地設(shè)計(jì)內(nèi)容矩肩,而不需要做什么適配现恼。但是由于densityDpi和xdpi,ydpi的實(shí)際差別黍檩,可以想到Android系統(tǒng)在把一堆dp設(shè)定的內(nèi)容映射為實(shí)際顯示的屏幕內(nèi)容的過(guò)程中叉袍,一定有基于densityDpi與xdpi,ydpi比例的換算過(guò)程刽酱。使得同一個(gè)顯示對(duì)象喳逛,在相同密度不同尺寸的手機(jī)上,擁有相同的高寬像素?cái)?shù)棵里,卻有不同的物理長(zhǎng)度艺配。
總結(jié),density衍慎,densityDpi转唉,scaledDensity是面向內(nèi)容的長(zhǎng)度單位(對(duì)程序猿友好,便于表達(dá)內(nèi)容)稳捆,xdpi赠法,ydpi是面向?qū)嶋H設(shè)備的參數(shù)(這是個(gè)連接了物理世界度量衡和Android度量衡的數(shù)值)。
PS:
我用上文的技術(shù)做了一個(gè)直尺的應(yīng)用乔夯,但是砖织,由于實(shí)際的手機(jī)xdpi和ydpi值居然不相等,所以末荐,顯示橫向的尺度相對(duì)來(lái)說(shuō)是比較準(zhǔn)確的侧纯,顯示縱向的尺度就不太準(zhǔn)了(因?yàn)槲矣肨ypedValue中MM的單位獲取長(zhǎng)度,而這個(gè)方法又是采用了xdpi來(lái)實(shí)現(xiàn)的甲脏,如果縱向繪制尺子的話眶熬,實(shí)際上得用ydpi來(lái)實(shí)現(xiàn)才對(duì)妹笆,不該用xdpi來(lái)做,除非xdpi和ydpi是相等的娜氏,而ydpi和xdpi不相等就會(huì)導(dǎo)致縱向繪制出現(xiàn)偏差拳缠,這個(gè)問(wèn)題后續(xù)可改正)。
注:
OS是overlapping sound的簡(jiǎn)稱贸弥,即內(nèi)心獨(dú)白窟坐,不是operating system!绵疲!