被設(shè)計師支配的恐懼
很多的入門程序猿,對于android自定義View庇楞,可能都是比較恐懼的,但是這又是高手進(jìn)階的必經(jīng)之路否纬÷郎危回想還是一個入門菜鳥時,每當(dāng)設(shè)計提出一個比較炫酷的動畫設(shè)計临燃,都會打開百度或者github先搜索一番睛驳。可是開源控件畢竟不是量身打造膜廊,與期望的效果總是有那么多的出入乏沸。這個時候就假裝一本正經(jīng)的說,這個沒法實現(xiàn)爪瓜。然后戰(zhàn)戰(zhàn)兢兢的等待設(shè)計那一句:“為什么ios的可以實現(xiàn)“蹬跃?
磨刀不誤砍柴工
android開發(fā)最讓人頭疼的是什么?我想應(yīng)該是適配各種機型铆铆。android不像ios般統(tǒng)一蝶缀,2012年到2014年支持Android設(shè)備的種類就從3997增長到18796。同時各大廠商定制的屏幕尺寸也非常多薄货。這導(dǎo)致有時候我們在一款機型上表現(xiàn)完美的自定義控件翁都,在另外一款手機上變形嚴(yán)重。所以在正式開始自定義控件之前谅猾,我們先回顧一下android中的各種尺寸
什么是屏幕尺寸柄慰、屏幕分辨率、屏幕像素密度赊瞬?
我的測試機是三星S5先煎,屏幕為5.2英寸∏山В看一下手機參數(shù):
拿它舉個栗子,代碼如下
DisplayMetricsmetrics = getResources().getDisplayMetrics();
int w = metrics.widthPixels;//寬度
int h = metrics.heightPixels;//高度
int dpi = metrics.densityDpi;//每英寸所占像素 (dpi:dots per inch )
int xdpi = metrics.xdpi;//x方向上的dpi
float density = metrics.density;//密度
Log.i("dimension","寬度:"+w+"\n高度:"+h+"\nDPI:"+dpi+"\nxDPI:"+xdpi+"\n密度:"+density);
log輸出
dimension: 寬度:1080
高度:1920
DPI:480
xDPI:422.03
密度:3.0
DPI每英寸點數(shù)
可以看到三星S5的寬1080px,高1920px遥倦。根據(jù)勾股定理可以算出對角線長2202.907px谤绳。
因為手機對角線尺寸為5.2英寸占锯,我們反向推導(dǎo)一下:
2202.907/5.2 = 423.6
與上面的xdpi相吻合。
所以dpi的概念一目了然缩筛,即每寸的像素有多少個點消略。面試的時候經(jīng)常有同學(xué)把dpi、dip瞎抛、dp弄混了艺演。下面我接著推。
密度density
我們首先知道桐臊,谷歌官方把android設(shè)備的參考標(biāo)準(zhǔn)定義為一寸是160px胎撤。既然谷歌的一寸是160px,為什么metrics返回的dpi是480呢断凶?伤提。這是因為android設(shè)備中160px每寸的密度隨著用戶需求的提高,漸漸無法滿足日常需要认烁。所以廠商將原本160個像素提高到480個像素(在該例中肿男,實際為422)。在同樣的空間下密度增大了三倍却嗡。所以480像素得到了解釋舶沛。由此我們又得到一個概念叫密度(density)
480/160 = 3
與上面的density相吻合。
獨立像素密度(密度無關(guān)像素)DIP/DP
dp可以說是我們?nèi)粘i_發(fā)中最常用到的長度單位了窗价。通過上面的計算我們可以得出冠王。
1dp = 1英寸/160
當(dāng)密度(density)為1時,1dp = 1px舌镶。
當(dāng)密度(density)為2時柱彻,1dp = 2px。
當(dāng)密度(density)為3時餐胀,1dp = 3px哟楷。
所以計算dp與px的轉(zhuǎn)換公式也十分簡單:
px = dp * density
或px = dp * (dpi/160)
常用單位一覽
px——屏幕上真實的像素。這是一個與像素密度有關(guān)聯(lián)的單位否灾,一px單位的物理大小取決于屏幕的像素密度卖擅。
in——屏幕上的物理英寸。這是一個與像素密度無關(guān)聯(lián)的單位墨技,一in單位的物理大小在任何像素密度的屏幕上都是一樣大的惩阶。一in單位轉(zhuǎn)化為多少px單位取決于屏幕的像素密度。
dp——像素密度無關(guān)聯(lián)的像素單位扣汪。這是一個與像素密度無關(guān)聯(lián)的單位断楷。然而一dp單位的物理大小在不同的像素密度屏幕上只是近視的相等。大約160dp等于一in崭别。在一dp轉(zhuǎn)化為160dpi中的一個比例因子是與設(shè)備的密度級別相關(guān)聯(lián)的冬筒。一dp等于多少像素取決于屏幕的像素密度和設(shè)備所屬的密度級別恐锣。
sp——大小獨立的像素單位,特地指定text的大小舞痰。這是一個與像素密度無關(guān)聯(lián)的單位土榴。然而一sp單位的物理大小在不同的像素密度屏幕上只是近視的相等。在一sp轉(zhuǎn)化為160dpi中的一個比例因子是與設(shè)備的密度級別以及字體表現(xiàn)的大小相關(guān)聯(lián)的响牛。一sp等于多少像素取決于屏幕的像素密度和設(shè)備所屬的密度級別玷禽。
除此之外,android還為我們提供了一些其他不常用的長度單位
mm——屏幕上的物理毫米呀打。這是一個與像素密度無關(guān)聯(lián)的單位矢赁,一mm單位的物理大小在任何像素密度的屏幕上都是一樣大的。25.4mm等于一in聚磺。一mm單位轉(zhuǎn)化為多少px單位取決于屏幕的像素密度坯台。
pt——點,屏幕上普通字體大小單位瘫寝。這是一個與像素密度無關(guān)聯(lián)的單位蜒蕾,一pt單位的物理大小在任何像素密度的屏幕上都是一樣大的。72pt等于一in焕阿。一pt單位轉(zhuǎn)化為多少px單位取決于屏幕的像素密度咪啡。
特別注意
事實上,我們屏幕上看到的東西都是由Paint繪制而來暮屡。而Paint對象接受的參數(shù)最后都是px像素來使用的撤摸。所以無論我們使用什么單位最后都轉(zhuǎn)換成了像素來處理。
所以android系統(tǒng)很貼心的給我們提供了TypedValue.applyDimension()方法提供單位轉(zhuǎn)換褒纲。點進(jìn)去可以看到:
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;
}```
事實上還是```px = dp * density```公式的變形准夷。有時候我們發(fā)現(xiàn)自定義View通過java代碼設(shè)置尺寸就是不能適配,但是通過屬性就可以莺掠。這里其實就是忘記調(diào)用TypedValue.applyDimension( unit, size, r.getDisplayMetrics())方法來把單位轉(zhuǎn)換成px
### 結(jié)語
好了衫嵌,雖然內(nèi)容不多。但是寫文章還是比我想象中困難很多彻秆。之前只是腦海里有這樣一個概念楔绞。但是要組織語言寫出來還是有障礙。再接再厲