你們在Android開發時,確定會以爲屏幕適配是個尤爲痛苦的事,各類屏幕尺寸適配起來蛋疼無比。若是咱們換個角度咱們看下這個問題,不知道你們 有沒有了解過web前端開發,或者說你們對於網頁都不陌生吧,其實適配的問題在web頁面的設計中理論上也存在,爲何這麼說呢?電腦的顯示器的分辨率、 包括手機分辨率,我敢說分辨率的種類遠超過Android設備的分辨率,那麼有一個很奇怪的現象:前端
爲何Web頁面設計人員歷來沒有說過,尼瑪適配好麻煩?java
那麼,究竟是什麼緣由,讓網頁的設計能夠在千差萬別的分辨率的分辨率中依舊能給用戶一個優質的體驗呢?帶着這個疑惑,我問了下媳婦(前端人員),媳 婦睜大眼睛問我:什麼叫適配?fc,尼瑪,看來的確沒有這類問題。後來再我仔細的追問後,她告訴我,噢,這個尺寸呀,我都是設置爲20%的~~追根到底, 其實就是一個緣由,網頁提供了百分比計算大小。android
一樣的,你們拿到UI給的設計圖之後,是否是抱怨過尼瑪你標識的都是px,我項目裏面用dp,這什麼玩意,和UI人員解釋,UI妹妹也不理解。那麼 本例一樣能夠解決Android工程師和UI妹妹間的矛盾~UI給出一個固定尺寸的設計稿,而後你在編寫佈局的時候不用思考,無腦照抄上面標識的像素值, 就能達到完美適配,理想豐不豐滿~~。git
然而,Android對於不一樣的屏幕給出的適配方案是dp,那麼dp與百分比的差距到底在哪裏?程序員
dpgithub
咱們首先看下dp的定義:web
Density-independent pixel (dp)獨立像素密度。標準是160dip.即1dp對應1個pixel,計算公式如:px = dp * (dpi / 160),屏幕密度越大,1dp對應 的像素點越多。
上面的公式中有個dpi,dpi爲DPI是Dots Per Inch(每英寸所打印的點數),也就是當設備的dpi爲160的時候1px=1dp;微信
好了,上述這些概念記不記得住不要緊,只要記住一點dp是與像素無關的,在實際使用中1dp大約等於1/160inch。網絡
那麼dp究竟解決了適配上的什麼問題?能夠看出1dp = 1/160inch;那麼它至少能解決一個問題,就是你在佈局文件寫某個View的寬和高爲160dp*160dp,這個View在任何分辨率的屏幕中, 顯示的尺寸大小是大約是一致的(可能不精確),大概是 1 inch * 1 inch。app
可是,這樣並不可以解決全部的適配問題:
呈現效果仍舊會有差別,僅僅是相近而已
當設備的物理尺寸存在差別的時候,dp就顯得無能爲力了。爲4.3寸屏幕準備的UI,運行在5.0寸的屏幕上,極可能在右側和下側存在大量的空白。而5.0寸的UI運行到4.3寸的設備上,極可能顯示不下。
以上兩點,來自參考連接1
一句話,總結下,dp可以讓同一數值在不一樣的分辨率展現出大體相同的尺寸大小。可是當設備的尺寸差別較大的時候,就無能爲力了。適配的問題還須要咱們本身去作,因而咱們可能會這麼作:
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- values-hdpi 480X800 --> <dimen name="imagewidth">120dip</dimen> </resources> <resources> <!-- values-hdpi-1280x800 --> <dimen name="imagewidth">220dip</dimen> </resources> <?xml version="1.0" encoding="utf-8"?> <resources> <!-- values-hdpi 480X320 --> <dimen name="imagewidth">80dip</dimen> </resources> 1234567891011121314151617
上述代碼片斷來自網絡,也就是說,咱們爲了優質的用戶體驗,依然須要去針對不一樣的dpi設置,編寫多套數值文件。
能夠看出,dp並無能解決適配問題。下面看百分比。
百分比
這個概念不用說了,web中支持控件的寬度能夠去參考父控件的寬度去設置百分比,最外層控件的寬度參考屏幕尺寸設置百分比,那麼其實中Android設備中,只須要支持控件可以參考屏幕的百分比去計算寬高就足夠了。
好比,我如今如下幾個需求:
對於圖片展現的Banner,爲了起到該有的效果,我但願在任何手機上顯示的高度爲屏幕高度的1/4
個人首頁分上下兩欄,我但願每一個欄目的屏幕高度爲11/24,中間間隔爲1/12
slidingmenu的寬度爲屏幕寬度的80%
固然了這僅僅是從一個大的層面上來講,其實小範圍佈局,可能百分比將會更加有用。
那麼如今不支持百分比,實現上述的需求,可能須要一、代碼去動態計算(不少人直接pass了,太麻煩);二、利用weight(weight必須依賴Linearlayout,並且並不能適用於任何場景)
再好比:個人某個浮動按鈕的高度和寬度但願是屏幕高度的1/12,個人某個Button的寬度但願是屏幕寬度的1/3。
上述的全部的需求,利用dp是沒法完成的,咱們但願控件的尺寸能夠按照下列方式編寫:
<Button android:text="@string/hello_world" android:layout_width="20%w" android:layout_height="10%h"/>1234
利用屏幕的寬和高的比例去定義View的寬和高。
好了,到此咱們能夠看到dp與百分比的區別,而百分比可以更好的解決咱們的適配問題。
some 適配tips
咱們再來看看一些適配的tips
多用match_parent
多用weight
自定義view解決
其實上述3點tip,歸根結底仍是利用百分比,match_parent至關於100%參考父控件;weight即按比例分配;自定義view無非是由於裏面多數尺寸是按照百分比計算的;
經過這些tips,咱們更加的看出若是能在Android中引入百分比的機制,將能解決大多數的適配問題,下面咱們就來看看如何可以讓Android支持百分比的概念。
其實咱們的解決方案,就是在項目中針對你所須要適配的手機屏幕的分辨率各自簡歷一個文件夾。
以下圖:
而後咱們根據一個基準,爲基準的意思就是:
好比480*320的分辨率爲基準
寬度爲320,將任何分辨率的寬度分爲320份,取值爲x1-x320
高度爲480,將任何分辨率的高度分爲480份,取值爲y1-y480
例如對於800*480的寬度480:
能夠看到x1 = 480 / 基準 = 480 / 320 = 1.5 ;
其餘分辨率相似~~
你可能會問,這麼多文件,難道咱們要手算,而後本身編寫?不要怕,下文會說。
那麼,你可能有個疑問,這麼寫有什麼好處呢?
假設我如今須要在屏幕中心有個按鈕,寬度和高度爲咱們屏幕寬度的1/2,我能夠怎麼編寫佈局文件呢?
<FrameLayout > <Button android:layout_gravity="center" android:gravity="center" android:text="@string/hello_world" android:layout_width="@dimen/x160" android:layout_height="@dimen/x160"/></FrameLayout>1234567891011
能夠看到咱們的寬度和高度定義爲x160,其實就是寬度的50%;
那麼效果圖:
能夠看到不論在什麼分辨率的機型,咱們的按鈕的寬和高始終是屏幕寬度的一半。
對於設計圖
假設如今的UI的設計圖是按照480*320設計的,且上面的寬和高的標識都是px的值,你能夠直接將px轉化爲x[1-320],y[1-480],這樣寫出的佈局基本就能夠全分辨率適配了。
你可能會問:設計師設計圖的分辨率不固定怎麼辦?下文會說~
對於上文提出的幾個dp作不到的
你能夠經過在引入百分比後,本身試試~~
好了,有個最主要的問題,咱們沒有說,就是分辨率這麼多,尼瑪難道咱們要本身計算,而後手寫?
好了,其實這樣的文件夾手寫也能夠,按照大家須要支持的分辨率,而後編寫一套,之後一直使用。
固然了,做爲程序員的咱們,怎麼能作這麼low的工做,確定要程序來實現:
那麼實現須要如下步驟:
分析須要的支持的分辨率
對於主流的分辨率我已經集成到了咱們的程序中,固然對於特殊的,你能夠經過參數指定。關於屏幕分辨率信息,能夠經過該網站查詢:http://screensiz.es/phone
編寫自動生成文件的程序
代碼以下
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(); } }123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
同時我提供了jar包,默認狀況下,雙擊便可生成,使用說明:
下載地址見文末,內置了經常使用的分辨率,默認基準爲480*320,固然對於特殊需求,經過命令行指定便可:
例如:基準 1280 * 800 ,額外支持尺寸:1152 * 735;4500 * 3200;
按照
java -jar xx.jar width height width,height_width,height
上述格式便可。
到此,咱們經過編寫一個工具,根據某基準尺寸,生成全部須要適配分辨率的values文件,作到了編寫佈局文件時,能夠參考屏幕的分辨率;在UI給出的設計圖,能夠快速的按照其標識的px單位進行編寫佈局。基本解決了適配的問題。
本方案思想已經有公司投入使用,我的認爲仍是很不錯的,若是你們有更好的方案來解決屏幕適配的問題,歡迎留言探討或者直接貼出好文連接,你們能夠將本身的經驗進行分享,這樣才能壯大咱們的隊伍~~。
注:本方案思想來自Android Day Day Up 一羣的【blue-深圳】,經其贊成編寫此文,上述程序也很大程度上借鑑了其分享的源碼。在此標識感謝,預祝其創業成功!
ok~
羣號:264950424,歡迎入羣
微信公衆號:hongyangAndroid
(歡迎關注,第一時間推送博文信息)