各種規格總結
// 首先闡釋一些術語和概念
屏幕尺寸(screen size): 屏幕的對角線測量。
為了方便,Android把所有的屏幕尺寸分為了4個廣義的大小:小、正常、大、更大
屏幕密度(screen density): 屏幕占據的物理區域所含像素的個數
通常被稱為dpi(dots per inch)即每英寸的像素點數
分辨率(resolution): 屏幕上物理像素的點數
例如,有一個240px*400px的屏幕,可以理解為在這個屏幕上橫着有400條線,每條線上有240個像素點
像素(px): 屏幕上的點
dip(dp):Density-independent pixel--->與密度無關的像素(下面將詳細講解)
由於JPG容易失真, 在Android開發中盡量避免使用.jpg圖片, 應該使用.png圖片, 它采用了從LZ77派生的無損數據壓縮算法.
補充
屏幕:尺寸5.1,分辨率1920X1080
DPI:1920^2+1080^2(v)/5.1=2202/5.1=431
mdpi 120dpi ~ 160dpi
hdpi 160dpi ~ 240dpi
xhdpi 240dpi ~ 320dpi
xxhdpi 320dpi ~ 480dpi
xxxhdpi 480dpi ~ 640dpi
在設計圖標時:
對於五種主流像素密度(MDIP, HDPI, XHDPI, XXHDPI, XXXHDPI)
應按照(2:3:4:6:8)的比例進行縮放,(1x, 1.5x, 2x, 3x, 4x)
例如:
尺寸為48x48dp的圖標,表示在MDPI的屏幕上其尺寸應為48x48px
在HDPI的屏幕上其實際大小是MDPI的1.5倍(72x72px)… 依此類推
字體:
對於字體的使用,官方不建議使用低於12號字體大小值
圖片內存占用:
drawable-xxhdpi 11.65M
drawable 74.97M
drawable-mhdpi 74.95M
drawable-hdpi 35.38M
在圖片密度不匹配的情況下,低分辨率圖片會被拉伸到高分辨率圖片
密度陷阱
雖然說dp可以去除不同像素密度的問題,使得1dp在不同像素密度上面的顯示效果相同,但是還是由於Android屏幕設備的多樣性
如果使用dp來作為度量單位,並不是所有的屏幕的寬度都是相同的dp長度
比如說:
Nexus S和Nexus One 屬於hdpi 屏幕寬度是320dp
Nexus 5 屬於xxhdpi 屏幕寬度是360dp
Galaxy Nexus 屬於xhdpi 屏幕寬度是384dp
Nexus 6 屬於xxxhdpi 屏幕寬度是410dp
所以說,光Google自己一家的產品就已經有這么多的標准,而且屏幕寬度和像素密度沒有任何關聯關系
即使我們使用dp,在320dp寬度的設備和410dp的設備上,還是會有90dp的差別。
當然,我們盡量使用match_parent和wrap_content,盡可能少的用dp來指定控件的具體長寬
再結合上權重,大部分的情況我們都是可以做到適配的。
此外還有一個解決辦法,也就是指定所有分辨率密度對應關系
// 下面是生成的一個320*480分辨率的文件
// 因為寬高分割之后總分數和像素數相同
// 所以x1就是1px,以此類推
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">1.0px</dimen>
<dimen name="x2">2.0px</dimen>
<dimen name="x3">3.0px</dimen>
<dimen name="x4">4.0px</dimen>
<dimen name="x5">5.0px</dimen>
<dimen name="x6">6.0px</dimen>
<dimen name="x7">7.0px</dimen>
<dimen name="x8">8.0px</dimen>
<dimen name="x9">9.0px</dimen>
<dimen name="x10">10.0px</dimen>
...省略好多行
<dimen name="x300">300.0px</dimen>
<dimen name="x301">301.0px</dimen>
<dimen name="x302">302.0px</dimen>
<dimen name="x303">303.0px</dimen>
<dimen name="x304">304.0px</dimen>
<dimen name="x305">305.0px</dimen>
<dimen name="x306">306.0px</dimen>
<dimen name="x307">307.0px</dimen>
<dimen name="x308">308.0px</dimen>
<dimen name="x309">309.0px</dimen>
<dimen name="x310">310.0px</dimen>
<dimen name="x311">311.0px</dimen>
<dimen name="x312">312.0px</dimen>
<dimen name="x313">313.0px</dimen>
<dimen name="x314">314.0px</dimen>
<dimen name="x315">315.0px</dimen>
<dimen name="x316">316.0px</dimen>
<dimen name="x317">317.0px</dimen>
<dimen name="x318">318.0px</dimen>
<dimen name="x319">319.0px</dimen>
<dimen name="x320">320px</dimen>
</resources>
// 那么1080*1960分辨率下是什么樣子呢?
// 我們可以看下,由於1080和320是3.37倍的關系
// 所以x1=3.37px
<?xml version="1.0" encoding="utf-8"?>
<resources><dimen name="x1">3.37px</dimen>
<dimen name="x2">6.75px</dimen>
<dimen name="x3">10.12px</dimen>
<dimen name="x4">13.5px</dimen>
<dimen name="x5">16.87px</dimen>
<dimen name="x6">20.25px</dimen>
<dimen name="x7">23.62px</dimen>
<dimen name="x8">27.0px</dimen>
<dimen name="x9">30.37px</dimen>
<dimen name="x10">33.75px</dimen>
...省略好多行
<dimen name="x300">1012.5px</dimen>
<dimen name="x301">1015.87px</dimen>
<dimen name="x302">1019.25px</dimen>
<dimen name="x303">1022.62px</dimen>
<dimen name="x304">1026.0px</dimen>
<dimen name="x305">1029.37px</dimen>
<dimen name="x306">1032.75px</dimen>
<dimen name="x307">1036.12px</dimen>
<dimen name="x308">1039.5px</dimen>
<dimen name="x309">1042.87px</dimen>
<dimen name="x310">1046.25px</dimen>
<dimen name="x311">1049.62px</dimen>
<dimen name="x312">1053.0px</dimen>
<dimen name="x313">1056.37px</dimen>
<dimen name="x314">1059.75px</dimen>
<dimen name="x315">1063.12px</dimen>
<dimen name="x316">1066.5px</dimen>
<dimen name="x317">1069.87px</dimen>
<dimen name="x318">1073.25px</dimen>
<dimen name="x319">1076.62px</dimen>
<dimen name="x320">1080px</dimen>
</resources>
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class MakeXml {
private final static String rootPath =
"C:\\Users\\Administrator\\Desktop\\layoutroot\\values-{0}x{1}\\";
private final static float dw = 320f;
private final static float dh = 480f;
private final static String WTemplate = "<dimen name=\"x{0}\">{1}px</dimen>\n";
private final static String HTemplate = "<dimen name=\"y{0}\">{1}px</dimen>\n";
public static void main(String[] args) {
makeString(320, 480);
makeString(480,800);
makeString(480, 854);
makeString(540, 960);
makeString(600, 1024);
makeString(720, 1184);
makeString(720, 1196);
makeString(720, 1280);
makeString(768, 1024);
makeString(800, 1280);
makeString(1080, 1812);
makeString(1080, 1920);
makeString(1440, 2560);
}
public static void makeString(int w, int h) {
StringBuffer sb = new StringBuffer();
sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
sb.append("<resources>");
float cellw = w / dw;
for (int i = 1; i < 320; i++) {
sb.append(WTemplate.replace("{0}", i + "").replace("{1}",
change(cellw * i) + ""));
}
sb.append(WTemplate.replace("{0}", "320").replace("{1}", w + ""));
sb.append("</resources>");
StringBuffer sb2 = new StringBuffer();
sb2.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
sb2.append("<resources>");
float cellh = h / dh;
for (int i = 1; i < 480; i++) {
sb2.append(HTemplate.replace("{0}", i + "").replace("{1}",
change(cellh * i) + ""));
}
sb2.append(HTemplate.replace("{0}", "480").replace("{1}", h + ""));
sb2.append("</resources>");
String path = rootPath.replace("{0}", h + "").replace("{1}", w + "");
File rootFile = new File(path);
if (!rootFile.exists()) {
rootFile.mkdirs();
}
File layxFile = new File(path + "lay_x.xml");
File layyFile = new File(path + "lay_y.xml");
try {
PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile));
pw.print(sb.toString());
pw.close();
pw = new PrintWriter(new FileOutputStream(layyFile));
pw.print(sb2.toString());
pw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static float change(float a) {
int temp = (int) (a * 100);
return temp / 100f;
}
}
應用程序圖標 (Icon)應當是一個 Alpha 通道透明的32位 PNG 圖片。
由於安卓設備眾多,一個應用程序圖標需要設計幾種不同大小,如:
LDPI (Low Density Screen,120 DPI),其圖標大小為 36 x 36 px。
MDPI (Medium Density Screen, 160 DPI),其圖標大小為 48 x 48 px。
HDPI (High Density Screen, 240 DPI),其圖標大小為 72 x 72 px。
xhdpi (Extra-high density screen, 320 DPI),其圖標大小為 96 x 96 px。
建議在設計過程中,在四周空出幾個像素點使得設計的圖標與其他圖標在視覺上一致,例如,
96 x 96 px 圖標可以畫圖區域大小可以設為 88 x 88 px, 四周留出4個像素用於填充(無底色)。
72 x 72 px 圖標可以畫圖區域大小可以設為 68 x 68 px, 四周留出2個像素用於填充(無底色)。
48 x 48 px 圖標可以畫圖區域大小可以設為 46 x 46 px, 四周留出1個像素用於填充(無底色)。
36 x 36 px 圖標可以畫圖區域大小可以設為 34 x 34 px, 四周留出1個像素用於填充(無底色)。
每英寸像素數, 可以反映屏幕的清晰度,用於縮放UI的
ldpi (low) ~120dpi
mdpi (medium) ~160dpi
hdpi (high) ~240dpi
xhdpi (extra-high) ~320dpi
xxhdpi (extra-extra-high) ~480dpi
xxxhdpi (extra-extra-extra-high) ~640dpi
drawable-ldpi 放低分辨率的圖片,即QVGA(240×320)
drawable-mdpi 放中分辨率的圖片,即HVGA(320×480)
drawable-hdpi 放高分辨率的圖片,如WVGA(480x800),FWVGA (480x854)。
drawable-xhdpi 放高分辨率的圖片,即720p(1280×720)
drawable-xxhdpi 放高分辨率的圖片,即1080p(1920×1080)
Android設備屏幕尺寸分布
上圖可以看出
對應normal尺寸的屏幕范圍集中在常見的3到5寸屏之間
large尺寸對應的就主要是5到7寸的nottpad之類的設備,例如三星的Note和Nexus7平板等
接下來是屏幕密度(dpi),需要說明的是,平時所說的屏幕分辨率其實不能作為屏幕適配的依據
應該依據屏幕密度和屏幕尺寸來換算,屏幕密度是指每英寸屏幕內容納的像素數
屏幕密度從ldpi到xhdpi分別對應為120dpi、160dpi、240dpi、320dpi
屏幕密度越高、分辨率越高、屏幕尺寸越小就產生了視網膜屏幕。
DIP單位詳解
Android規定一個dip的大小相當於160dpi屏幕上的一個像素
它是系統為“中等的”密度屏設定的基准密度,在不同dpi屏幕上dp對應的像素數是不同的
需要時,基於當前屏的實際密度,系統會透明地放縮dip單。
dip單位根據公式像素值 = [dip*(dpi/160)](px)(其中px是單位)轉化為屏幕像素。
根據此公式可以計算出一個dip分別在120dpi、160dpi、240dpi、320dpi
屏幕中對應的像素數分別為0.75、1、1.5、2.0,比例為3:4:6:8。
因此,在不同屏幕密度上,以mdpi作為基准,對位圖進行3:4:6:8比例的放縮會達到適配的效果。
dip與一般的px不太一樣,它是獨立於屏幕密度的。什么是獨立於密度?
先來說下一般的px,如果將一個相同長寬像素的圖片放在不同屏幕密度大小的屏幕中
那么,在低密度屏幕中圖片會顯示的很大,在高密度屏幕中則會顯示的很小;
但是,如果使用dip為單位的圖片顯示的效果則是,屏幕密度越大的手機,圖片顯示的像素也相應增大
這樣在屏幕密度大的手機和屏幕密度小的手機上,圖片看上去大小基本相同。
有了上文對dip的講解,是否對這個現象有所理解呢?
舉個例子來說一下:
現在有三個物理長寬分別為3寸、4寸,屏幕密度分別為120dpi、160dpi、240dpi的手機
則三個屏幕的分辨率分別為360px*480px、480px*640px、
將三個手機屏幕的寬分為三等份,則根據dpi的定義,三個屏幕中每等份分別容納120px、160px、240px。
現在假設有一個控件imageview 它的長寬分別為160px、160px,還有一個160px*160px的圖片資源
當程序運行時,該圖片在三個屏幕上會呈現以下效果:
如果將imageview的長寬分別改為160dip、160dip,圖片將在三個屏幕上呈現以下效果:
上文提到在這三種屏幕密度下一個dip分別對應0.75px、1px、1.5px
所以在三種屏幕上該圖片占據120px、160px、240px,各自占屏幕的三分之一,所以看起來是一樣大的。
由上文可總結出Android在適配不同屏幕密度時,可以用dip作為控件的單位,視情況放縮dip單位。
當應用沒有指出圖片對應的控件的大小,Android是如何讓圖片適配不同屏幕的呢?
在Android2.1之前,開發應用時只有一個放圖片資源的drawable文件夾,這樣程序在不同屏幕密度的手機上運行時
系統只會從drawable這個文件夾下調圖片資源,並且系統會默認認為這個文件夾下的所有資源是為mdpi屏幕提供的
所以在hdpi屏幕上系統會按比例將drawable下的圖片擴大為原來的1.5倍
在ldpi屏幕上系統會按比例將drawable下的圖片縮小為原來的0.75倍
這樣會大大降低頁面效果。
在Android2.1以及之后,出現了drawable-ldpi、drawable-mdpi、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi。
在這些文件下提供的圖片大小最好是3:4:6:8:12。
程序在不同的屏幕密度下運行時,會首先去符合當前屏幕密度的文件夾下找對應的資源
如果沒有,系統會以最省力為前提去別的文件夾下找對應的資源並對其進行相應的縮放
如果還沒有,就回去默認的drawable文件夾下找,然后按照2.1之前的規則縮放
如果還沒有找到,應用就會報錯或者直接crash掉了。
舉個例子
現在有一個ldpi的手機屏幕,有一個應用在其上運行(假如只有ldpi、mdpi、hdpi還有drawable四個存放圖片的文件夾)
並需要調用一個圖片a.png(在下文中用a來代替a.png)。Android系統會經歷以下流程:
將hdpi中的圖片大小縮小為原來的一半相比將mdpi中的圖片大小縮小為原來的3/4,計算機要省力,只需進行簡單地右移一位操作。
所以系統在ldpi下找不到a的時候會首先去hdpi下去找。當存在xhdpi、xxhdpi時,系統會按相同的規則去調用資源。
Drawable-ldpi 3、Drawable-mdpi 4、Drawable-hdpi 6中的3、4、6指的是同一個圖片在三個文件夾下的大小之比。
Android開發者在做圖片適配時需要注意一下兩點
盛放圖片的控件要用dip單位來定義其長寬。
最好在ldpi、mdpi、hdpi、xhdpi、xxhdpi文件夾下提供大小比例為3:4:6:8:12的圖片。
當然如果有質量好的.9.png圖片的話,提供一個也可以。
筆記
Google 提供的 PX 轉 DP 的公式 ( px * 160 ) / PPI 獲取 DP 值
通過測試,Android 系統是使用默認 DP 來作為基數,而不是使用實際手機中的 DPI ,只是取相近的像素值
比如 5寸手機 1920X1080 分辨率 DP 為 440 ,但是 Android 手機使用是 480 的 DP
1920X1080 為基數 3倍數 而 1280X720 為基數 2倍數
所以現在遇到兩個問題,一個設計給的PX我轉換成DP則使用
PX * 160 / 480 后的值作為 像素間距大小 等值
另外一個就是發現非XML文件中設置DP即使進行了轉換也與設計效果相差很多
所以在這種情況下使用 百分比進行計算
讀取手機真是分辨率 寬 / 百分比 和 高 / 百分比
dip : device independent pixels 設備無關像素(dp就是dip)
dpi :dots per inch ,密度(一英寸多少個點)