理一理螢幕尺寸那些事
注:本文的目的在於理清楚一些尺寸關係,如果有表述不當,歡迎指出討論
一、科普常識:
0.測試準備
手上有兩個真機: oppoA77(
1920*1080 5.5英寸
)、 oppoR15X(2340*1080 6.4英寸
) 、
再加一臺模擬器(480*320 3.5英寸
)仿OPPO R801
輔助:一臺膝上型電腦聯想Y480N(768*1366 14英寸
) 和一個iPad_Air_2(2048*1536 9.7英寸
)
1.按照畫素來算引出的問題(反證法):
如果
,我說如果
一個畫素代表n個物理毫米,當n等於1時
那麼oppoR15X(2340*1080
)相當於23.4*10.8cm
,量了一下,大概跟書差不多
OPPO R801(480*320
)相當於(4.8*3.2cm
),量了一下,差不多跟橡皮一樣大
在同一參考系下,玩oppoR15X和OPPO R801,相當於玩一本書和玩橡皮的區別
顯然我並沒有這樣的體驗,這隻能說明,對於兩個不同的手機,它們的n值不同
也就是兩個手機:1個物理毫米中所含的畫素個數是不同的
2.手機英寸的概念
英寸是衡量手機螢幕的真實大小
我們買手機一般關心的是手機是多少多少英寸的,然後懂行的看看解析度,那英寸代表什麼?
1英寸 = 2.54 釐米
: 大概和一元硬幣的直徑差不多,不信你拿六個硬幣排在對角線比一下
由於解析度確定了長寬比,我們不難算出oppoR15X和OPPO R801的物理寬高
\ | 寬px | 高px | 對角線in | 對角線cm | 物理寬cm | 物理高cm |
---|---|---|---|---|---|---|
oppoR15X | 1080px | 2340px | 6.4in | 16.256cm | 6.81cm | 14.76cm |
OPPO R801 | 320px | 480px | 3.5in | 8.89cm | 4.93cm | 7.40cm |
3.螢幕屬性的封裝
這麼多屬性,一個一個算也怪累人的,計算器點著也不爽,封裝一下吧
ScreenInfo.java
/**
* 作者:張風捷特烈<br/>
* 時間:2018/12/1 0001:8:01<br/>
* 郵箱:[email protected]<br/>
* 說明:螢幕尺寸資訊
*/
public class ScreenInfo {
public static final float INCH_TO_MM = 25.399999961392f;//英寸轉為毫米數
public String name;//裝置名稱
public float inchC;//英寸
public int pxW;//螢幕寬畫素數
public int pxH;//螢幕高畫素數
public int pxC;//對角線畫素數
public float ppi;
public float dpi;
public float relW;//實際寬度
public float relH;//實際高度
public float relC;//實際對角線長度
public ScreenInfo() {
}
public ScreenInfo(String name, float inchC, int pxH, int pxW) {
this.name = name;
this.inchC = inchC;
this.pxW = pxW;
this.pxH = pxH;
pxC();
ppi();
dpi();
relC();
relW();
relH();
}
private int pxC() {
pxC = (int) Math.sqrt(pxH * pxH + pxW * pxW);
return pxC;
}
private float ppi() {
ppi = pxC() / inchC;
return ppi;
}
private float dpi() {
dpi = pxC() / inchC;
return dpi;
}
private float relW() {
relW = relC * (pxW * 1.f / pxC());
return relW;
}
private float relH() {
relH = relC * (pxH * 1.f / pxC());
return relH;
}
private float relC() {
relC = inchC * INCH_TO_MM;
return relC;
}
@Override
public String toString() {
return "ScreenInfo{" +
"\nname='" + name + '\'' +
"\n, 螢幕尺寸/英寸=" + inchC +
"\n, 螢幕橫向畫素數=" + pxW +
"\n, 螢幕縱向畫素數=" + pxH +
"\n, 螢幕對角線畫素數=" + pxC +
"\n, ppi=" + ppi +
"\n, dpi=" + dpi +
"\n, 螢幕對角線物理尺寸/mm=" + relC +
"\n, 螢幕橫向物理尺寸/mm=" + relW +
"\n, 螢幕縱向物理尺寸/mm=" + relH +
'}';
}
}
複製程式碼
好吧,知道大家不喜歡看資料,於是我自定義了一個View,用ScreenInfo資訊畫個圖示,感覺還蠻好的
三行程式碼就能畫一個手機資訊圖,也不是很難,有興趣的可以看看原始碼,或自己畫畫
我是按照物理尺寸畫的,所以現實中它們的螢幕
相對大小就是這樣的!
如果你想玩,其他的螢幕也可以試試:只要知道解析度和多少英寸
4.密度:
什麼是密度?----緊密程度?
上學的時候應該聽過線密度,面密度和體密度、或者人口密度吧。
比如一個市的人口密度:合肥市面積為11445.1平方公里,人口為779萬,人口密度為680.6人/平方公里
也就是合肥市平均 1平方公里 有680.6人
複製程式碼
現在把螢幕當做土地,把畫素點當做人,這些人一個一個有秩序地排著
那麼OPPOR15X中1mm的長度排多少個畫素,顯而易見:2577px/163mm = 15.809... 取個整 15.8
那麼 1 平方毫米能容下幾個畫素呢? 15.8*15.8 = 249.64個/mm^2 約250個/mm^2
那麼OPPOR801中1mm的長度排多少個畫素,顯而易見:2577px/163mm = 6.471... 取個整 6.5
那麼 1 平方毫米能容下幾個畫素呢? 6.5*6.5 = 42.25個/mm^2 約42個/mm^2
相當於在一片等大的土地上,一塊佔了250個人,一塊佔了42個人,神奇的是兩邊都把這塊地佔滿了
於是真相(得出的結論)只有一個:兩塊土地上一塊是小人,一塊是巨人
複製程式碼
現在把物理尺寸:1mm的螢幕放大
記得小時候的手機肉眼就能看到一點一點的畫素,就是因為1mm裡的畫素點少,相對而言一粒畫素就大
現在的手機可是瞅不出來畫素了
5.ppi與dip
現在我們手上的資訊還蠻多的,這些資訊有什麼用?
ppi(Pixel Per Inch),即每英寸的畫素。
我們剛才好像算了每毫米的畫素數,那每英寸的畫素數能難倒你嗎?
OPPO-R15X 的 ppi : 2577px/6.4in = 402.65625 px/in 約402.6ppi
OPPO-A77 的 ppi : 2202px/5.5in = 400.363... px/in 約400.4ppi
OPPO-R801 的 ppi : 576px/3.5in = 164.571... px/in 約164.6ppi
複製程式碼
ppi形象一點的比喻:
一個一元硬幣直徑約1 in,現在讓一元硬幣(包括背景)等大顯示在三個手機上:
OPPO-R15X需要用:402*402=161604 個畫素點
OPPO-A77需要用:400*400=160000 個畫素點
OPPO-R15X需要用:164*164=26896 個畫素點
我們知道畫素組成了顯示的圖片,也就是說用161604個點和26896個點組成相同的畫面
那麼26896的那個看起來效果自然要比161604的差很多,161604更加緊密,所以視覺感好
來分析一下膝上型電腦:
ppi=1567/14=111.928...
,也就是 1 in 裡有112個點,1 in = 25.4mm
那說明一個畫素的大小是25.4 / 112 = 0.226mm
,人眼可視長度是0.1mm,所以你近些看可以看到顆粒
dpi(Dot Per Inch),即每英寸的點數。
dpi又是什麼鬼,點數又是什麼鬼?---dpi稱為列印精度
印表機將[彩色液體油墨]經噴嘴變成細小微粒噴到印紙上,一個顆粒代表1點
dpi的意思是每英寸墨滴點數,比如300dpi的意思就是每英寸墨滴的個數為300
用300個點表示一個硬幣,和72個點表示一個硬幣,可想而知300的更加精細
大學時做要列印的ps產品效果圖都要把圖片的dpi調到300以上,因為大幅的海報需要細緻的畫素表達
普通的web圖片只要求72dpi就夠了,因為只是顯示在螢幕上而言
複製程式碼
ppi和dpi在Android
Android又不是印表機,dpi和ppi等價,都是表示 1 in長度對應的px數
也許谷歌更傾向於用`點(dot)` 來表述螢幕畫素,所以採用dpi的說法而不是ppi
複製程式碼
二、誰動了我的圖片尺寸?
1.我的250*200的圖片
畫出來怎麼尺寸不對?----Q1
實驗圖片:
250*200,300dpi,res\mipmap-xhdpi\wy_250px_200px_300dpi.jpg
實驗圖片:
250*200,72dpi,res\mipmap-xhdpi\wy_250px_200px_72dpi.jpg
這是挺糾結的一個問題,我預想的是在小手機上圖片250px應該會很大
為什麼並不是我所預料的那樣?而且自定義的圖片dpi被無視了?----Q2
2.我懷著滿心的疑問將圖片拷貝到drawable裡
實驗圖片:
250*200,72dpi,res\drawable\wy_250px_200px_72dpi.jpg
實驗圖片:
250*200,300dpi,res\drawable\wy_250px_200px_300dpi.jpg
貌似對dpi還是免疫,而且OPPO-R15X圖片按比例放大了,分別彈了一個
metric.density
,一個3,一個1
哥就想都顯示250*200為什麼這麼難?----Q3
3.懷著疑問,分別將圖片在mipmap各個資料夾放一遍
測試結果如下,並畫了一張高度變動的簡單示意圖
可以看出:
1.OPPO-R15X在xxh的時候顯示原圖,OPPO-R801在m的時候顯示原圖
2.每種情況下OPPO-R15X的高度總是OPPO-R801的3倍
結論:OPPO-R15X自身dpi(ppi)為402,被圈入了xxh的領域,OPPO-R801自身dpi(ppi)為164,被圈入了m的領域
xxh對應的dpi/m對應的dpi = 3
複製程式碼
Q1:誰動了我的圖片尺寸
---mipmap的不同資料夾,Android會區分對待
Q2:而且自定義的圖片dpi被無視了?
----圖片自身的dpi對螢幕裝置的顯示並沒有效果,只對列印有影響
Q3:哥就想都顯示250*200為什麼這麼難?
---- 難! 但是也沒有這個必要,你非怎麼想,在所有的mipmap資料夾都放一張圖,
然後所有手機會顯示250*200,不過有人打你不關我事...
複製程式碼
4.獲取不在mipmap裡的圖片會怎麼樣?
這個問題問的好,程式碼測試走一波
不出所料,從檔案讀取的圖片,沒走mipmap,所以原畫素顯示
總結:mipmap會根據圖片的資料夾位置對圖片在不同density裝置上進行不同的縮放,也就是"自動適配"
只限獲取圖片時或使用時warp_content
三、看看那些尺寸
1.dp:
想必大家這個方法都用過
protected float dp(float dp) {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
複製程式碼
來看看它到底幹了什麼:applyDimension
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;//將值*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;
}
複製程式碼
程式碼追蹤:
--> getResources().getDisplayMetrics()
-->[android.content.res.Resources#getDisplayMetrics]
public DisplayMetrics getDisplayMetrics() {
return mResourcesImpl.getDisplayMetrics();
}
-->[android.content.res.ResourcesImpl#getDisplayMetrics]
DisplayMetrics getDisplayMetrics() {
if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
+ "x" + mMetrics.heightPixels + " " + mMetrics.density);
return mMetrics;
}
-->[現在要看: mMetrics.density被賦值時]
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
mMetrics.density =
mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
}
搜尋到:mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
複製程式碼
我們被奉為靈丹妙藥的dp只不過獲取了
DisplayMetrics.DENSITY_DEVICE
,再乘以0.00625(即除以160)
而且這個DisplayMetrics.DENSITY_DEVICE
也並非螢幕真正的dpi(ppi),400的,402的都算是480,
所以dp的計算方式也是一個滿足大眾需要的約值,雖然有一定的效果,但並不能完美適配。
真的有完美的適配嗎?
除非你能讓
長寬是100*180的紙片
能夠恰好裝滿長寬是100*200盒子
而且沒有變形
或者讓所有的安卓手機廠家生產相同比例的手機,否則無論怎麼配會有瑕疵,
魚和熊掌不可兼得
,但捨生和取義之間還有平常地活著
(儘管並不完美)
我曾經有想過,為什麼手機不是圓形的,如果是圓形的就不需要適配了,永遠等比例
也許兜裡放著佔地方吧,或者看電影,可用面積會比較少...感覺好可惜
複製程式碼
文字單位:sp
-->[android.util.TypedValue#applyDimension]
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
-->[android.content.res.ResourcesImpl#updateConfiguration]
mMetrics.scaledDensity = mMetrics.density *
(mConfiguration.fontScale != 0 ? mConfiguration.fontScale : 1.0f);
//現在焦點移到:mConfiguration.fontScale != 0
//如果是假的,那就和mMetrics.density的值相同,否則是mConfiguration.fontScale
複製程式碼
其中configuration.fontScale是根據系統字號改變的,預設是1,所以會遇到dp和sp混用無影響的情況。但,一旦使用者改變了系統字號,有一定的縮放量,dp的為sp就原形畢露了,所以字型還是乖乖用sp,別沒事找事。
關於適配
個人覺得只要多用控制元件之間進行尺寸依賴關聯,也就是約束佈局
dp雖然不是靈丹妙藥,但是也能治病救人,所以藥不能停,dp不能丟
match_parent是好東西,要懂得合理運用
注意左側和下側,儘量用父去約束,不然跑出去了……可是大忌 儘量避免使用非常大的dp(200+),可通過控制元件間相對位置將過大的dp約束,因為數值越大不同手機的差異性越明顯。
後記:捷文規範
1.本文成長記錄及勘誤表
專案原始碼 | 日期 | 備註 |
---|---|---|
V0.1--github | 2018-12-4 | 理一理螢幕尺寸那些事 |
2.更多關於我
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
我的github | 我的簡書 | 我的掘金 | 個人網站 |
3.宣告
1----本文由張風捷特烈原創,轉載請註明
2----歡迎廣大程式設計愛好者共同交流
3----個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
4----看到這裡,我在此感謝你的喜歡與支援