標籤:
夏至 歡迎轉載,也請保留這段申明,謝謝
以前在做小東西的時候,覺得只要適應了自己的手機就可以了,發現真的給自己挖了很多坑。今天終於好好地研究了一下,這是個痛苦的事情,但是卻又真的很有趣。
首先,先理解幾個概念。
1、螢幕尺寸
螢幕尺寸指螢幕的對角線的長度,單位是英寸 : 1 英寸 = 2.54 mm 常見的尺寸有: 2.4 、2.8、3.5 、3.7,4.2、5.0等等
2、螢幕解析度
螢幕解析度是指在橫縱向上的像素點數,單位是px ,1px = 1個像素點
3、螢幕像素密度
螢幕像素密度是指每 英寸上的像素點數,單位是dpi (也叫dp),螢幕像素密度與螢幕尺寸和螢幕解析度有關,在單一變化條件下,螢幕尺寸越小,解析度越高,像素密度越大,反之越小。
4、dp、dip、dpi、sp、px
dip 和 dp 是一個意思,即螢幕密度。至於 dpi 是什嗎?可以這樣理解,假如 1 英寸 裡面有160個像素,那麼這個螢幕的像素密度就是160dpi。在這種情況下,dp 和 px 換算非常簡單,如所示:
比如你在一個480x320的手機上布好了局,一看霸道炫酷,但你的單位是用px的,一到了800x480或者更高解析度的手機的時候,就是東倒西歪的了。所以,我們一般不建議用px做單位。
5、mdpi 、hdpi、xdpi、xxdpi
中還有個ldpi 的,不過現在基本沒人用這麼慘不忍睹的解析度了,所以就可以不用去管它了。用eclipse的我們經常會在 res 檔案夾中看到這樣定義的 drawable 檔案夾,那麼它們究竟是什麼意思呢?Google 官方指定按照下列標準進行區分:
在進行開發的時候,我們也需要對應不同的檔案夾放入不同的圖片:
如果是對縮放不是特別重要的,我們可以把它放在drawable檔案夾中,比如.9圖等
6、支援屏邊螢幕尺寸
回到我們的重點,我們今天要講的就是螢幕的自適應。
- 使用wrap_content、match_parent、weight
為了確保布局的靈活性並適應各種不同的螢幕,我們一般建議使用 wrap_content 內容包裹,和match_parent 填充整個容器,這樣就是不是絕對布局,而是可以根據螢幕的大小而自適應,但我們布局那麼多,不可能每個都用這兩個屬性,所以,局限性也很大。
至於 weight 權重這個屬性呢,好用是好用,不過一般是線上性布局中,但線性布局有個尷尬的地方就是,如果你的布局很多,那麼它的樹根就會很長,這個對於 UI 的最佳化是很不利的,而Google一般建議我們的布局層數不要超過10層,所以,如果是相對比較複雜的,都是建議使用 Reativelayout 相對布局。所以,局限也很大。
- 使用 dp 和百分比
這個時候,你可能會說,使用 dp 和 sp 啊,dp 是密度,你解析度不同,用 dp 也能很好的適應。 在網上找到兩張圖。使用相同的代碼在兩種不同的機型上啟動並執行效果如下:
很尷尬是不是,都使用了 dp 了,為什麼還會出現這樣的問題?其實很簡單,因為螢幕的多樣性,並不是所有的螢幕都是相同的 dp 長度,比如 Nexus One 屬於hdpi ,螢幕寬度是320dp,而 Nexus 5屬於xxhdpi ,螢幕寬度是360dp。所以,就算你用了dp也一樣存在問題。比如,你在320 dp 裡,寫了一個TextView ,字型大小為24 sp,在兩個不同的裝置下,大小顯而易見的不一樣。
這個時候,你是不是很絕望,感覺生無可戀的感覺。解析度不一樣,所以不能用px;螢幕寬度不一樣,所以不能用 dp ,還讓不讓人活了。別急,還是有解決方案的。參考兩位大神的神作:
鴻洋大神的:http://blog.csdn.net/lmj623565791/article/details/45460089
一葉飄舟的:http://blog.csdn.net/jdsjlzx/article/details/45891551
基本看了它們的你就會了。我自己只是把我的理解也寫一下而已。
當然,在安卓 5.0 之後,安卓也支持度百分比的了。這無疑是個好訊息。大家可以看到,為什麼我們在電腦瀏覽網頁什麼的,那些控制項大小,總能根據我們螢幕大小的不同而變化,就是人家用了百分比的效果啊,百分比就是佔據整個螢幕的比例,這個很容易懂吧。但是它的局限性就是在也在這裡,需5.0系統,我自己試了一下,效果真心不錯,但是放到4.3的系統,就亂套了。所以,可以適當瞭解。如果你想更近一步的瞭解,請參考鴻洋大神的作品:http://blog.csdn.net/lmj623565791/article/details/46695347
好了,言歸正傳,我們的方案很簡單,就是在項目中添加你的解析度的不同檔案夾,如:
自動產生 解析度包的 Java 代碼如下:
import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.PrintWriter;/** * Created by zhy on 15/5/3. */public class GenerateValueFiles { private int baseW; private int baseH; private String dirStr = "./res"; 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"; /** * {0}-HEIGHT */ private final static String VALUE_TEMPLATE = "values-{0}x{1}"; private static final String SUPPORT_DIMESION = "320,480;480,800;480,854;540,960;600,1024;720,1184;720,1196;720,1280;768,1024;800,1280;1080,1812;1080,1920;1440,2560;"; private String supportStr = SUPPORT_DIMESION; public GenerateValueFiles(int baseX, int baseY, String supportStr) { this.baseW = baseX; this.baseH = baseY; if (!this.supportStr.contains(baseX + "," + baseY)) { this.supportStr += baseX + "," + baseY + ";"; } this.supportStr += validateInput(supportStr); System.out.println(supportStr); File dir = new File(dirStr); if (!dir.exists()) { dir.mkdir(); } System.out.println(dir.getAbsoluteFile()); } /** * @param supportStr * w,h_...w,h; * @return */ private String validateInput(String supportStr) { StringBuffer sb = new StringBuffer(); String[] vals = supportStr.split("_"); int w = -1; int h = -1; String[] wh; for (String val : vals) { try { if (val == null || val.trim().length() == 0) continue; wh = val.split(","); w = Integer.parseInt(wh[0]); h = Integer.parseInt(wh[1]); } catch (Exception e) { System.out.println("skip invalidate params : w,h = " + val); continue; } sb.append(w + "," + h + ";"); } return sb.toString(); } public void generate() { String[] vals = supportStr.split(";"); for (String val : vals) { String[] wh = val.split(","); generateXmlFile(Integer.parseInt(wh[0]), Integer.parseInt(wh[1])); } } private void generateXmlFile(int w, int h) { StringBuffer sbForWidth = new StringBuffer(); sbForWidth.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); sbForWidth.append("<resources>"); float cellw = w * 1.0f / baseW; System.out.println("width : " + w + "," + baseW + "," + cellw); for (int i = 1; i < baseW; i++) { sbForWidth.append(WTemplate.replace("{0}", i + "").replace("{1}", change(cellw * i) + "")); } sbForWidth.append(WTemplate.replace("{0}", baseW + "").replace("{1}", w + "")); sbForWidth.append("</resources>"); StringBuffer sbForHeight = new StringBuffer(); sbForHeight.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); sbForHeight.append("<resources>"); float cellh = h *1.0f/ baseH; System.out.println("height : "+ h + "," + baseH + "," + cellh); for (int i = 1; i < baseH; i++) { sbForHeight.append(HTemplate.replace("{0}", i + "").replace("{1}", change(cellh * i) + "")); } sbForHeight.append(HTemplate.replace("{0}", baseH + "").replace("{1}", h + "")); sbForHeight.append("</resources>"); File fileDir = new File(dirStr + File.separator + VALUE_TEMPLATE.replace("{0}", h + "")// .replace("{1}", w + "")); fileDir.mkdir(); File layxFile = new File(fileDir.getAbsolutePath(), "lay_x.xml"); File layyFile = new File(fileDir.getAbsolutePath(), "lay_y.xml"); try { PrintWriter pw = new PrintWriter(new FileOutputStream(layxFile)); pw.print(sbForWidth.toString()); pw.close(); pw = new PrintWriter(new FileOutputStream(layyFile)); pw.print(sbForHeight.toString()); pw.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } } public static float change(float a) { int temp = (int) (a * 100); return temp / 100f; } public static void main(String[] args) { int baseW = 320; int baseH = 400; String addition = ""; try { if (args.length >= 3) { baseW = Integer.parseInt(args[0]); baseH = Integer.parseInt(args[1]); addition = args[2]; } else if (args.length >= 2) { baseW = Integer.parseInt(args[0]); baseH = Integer.parseInt(args[1]); } else if (args.length >= 1) { addition = args[0]; } } catch (NumberFormatException e) { System.err .println("right input params : java -jar xxx.jar width height w,h_w,h_..._w,h;"); e.printStackTrace(); System.exit(-1); } new GenerateValueFiles(baseW, baseH, addition).generate(); }}
要修改的就一個地方,即基準,比如上面的是以 480 x 320 為基準的。,我們可以改成我們所需要的。當然,你也可以產生 jar 包,然後在命令列下,產生不同的代碼:
先把過程打包成jar包,然後輸入:
Java -jar xx.jar width height width,height_width,height
什麼意思呢,前面兩個是基準的意思。後面的是你要額外擴充的包。比如
我要一個1280x720 的基準,再額外擴充 一個 1366x768的包。就可以這樣寫:
java -jar test.jar 1280x720 1366,768
後面也是要擴充的,這裡可以根據不同而選擇不同。、
當然你也可以直接你要的基準的包:
但這裡也有局限性,看到網上很多螢幕說,如果帶有虛擬鍵的手機,這個方案也不適合了,只能跟著虛擬鍵來重新適配,當然這裡還是適用於很多記性的。這裡把常用的兩個基準 480x320 、800x480,和1280x720 基準的res包發給大家,當然我建議你自己弄。CSDN 的附件太蛋疼了,用360好了:
160 基準 https://yunpan.cn/cPFiutXhwhdpB 訪問密碼 1bc7
240 基準 https://yunpan.cn/cPFiFXihU7ytz 訪問密碼 29df
320 基準 https://yunpan.cn/cPFixBsSQqrrX 訪問密碼 91da
如果消失了,請聯絡我。
7、安卓 TV 跟手機的不同
這裡我們要 講講安卓TV 跟手機的不同。在手機上,我們可以以一個480x320的基準來設計螢幕大小,因為手機撐死的就5.0寸,所以,這個基準還是非常不錯的。但是安卓TV不一樣,都是平板類的,而且是比較大的液晶屏。所以,我們的基準就是要變一下。要用1280x720 的為基準,當然,這個只是個人理解。有錯誤也歡迎指出。
進階之 -- 螢幕自適應