最清晰的Android多屏幕適配方案

問題的引入


 

當您的Android應用即將發佈的時候,若是你想讓更多的用戶去使用你的應用,擺在工程師面前的一個重要問題就是如何讓你的應用能在各類各樣的終端上運行,這裏的各類各樣首當其衝的就是不一樣的屏幕分辨率和尺寸。java

 
屏幕適配主要從圖片和 距離(文字) 進行下手。從以往的方式適配方式中,開發者可能會考慮各類各樣的分辨率,好比480*800、1280*800、1920*1080等,爲此在資源文件夾裏面建立了一大堆子文件夾,那麼有什麼更好的方式嗎?首先咱們看下genymotion模擬其中一些流行的鏡像的屏幕參數信息:
 
上面舉了兩個例子,其實看了好幾個發現大部分的屏幕雖然分辨率各類各樣,可是dpi最多的就三種:160dpi、240dpi、320dpi
因此爲了屏幕適配,咱們應該僅僅對dpi作適配便可,這裏咱們分別對 160dpi、240dpi、320dpi進行適配便可知足市場上大部分的需求,以上是圖片的解決方案。
 
同時,光看dpi也不行,也要看對應的屏幕最小寬度,這個最小寬度是和dp對應的,用下列代碼既可:
Configuration config = getResources().getConfiguration();
int smallestScreenWidth = config.smallestScreenWidthDp;
L.i("smallest width : "+ smallestScreenWidth);

 

這個獲取出來的值,咱們須要創建對應的values-sw{smallestWidth}dp文件夾進行適配,以上是文字和距離尺寸的適配。
 
下面將具體的距離說明,文中的例子原先是在1280*800的160dpi的屏幕上開發的,這個在這裏被叫作 base size ,咱們對這個項目的代碼進行適配。
 
 

文字和尺寸的適配


咱們這裏須要將代碼跑在一個1920*1200分辨率320dpi的平板上,發現全部的字體都變大了,看似1920*1200的分辨率比以前的 1280*800要大一大圈,可是由於dpi也高,因此致使字體變大。
運行上面的獲取smallestScreenWidth的代碼後,發現值爲600。(base size的平板電腦這個值是800)
 
首先在values文件夾中創建一個dimens.xml文件
 
繼續在res中創建和values文件夾同級別的兩個文件夾values-sw600dp-land和values-sw800dp-land,爲了適應更多的屏幕,也加入了values-sw480dp-land (後綴是land是由於例子的項目是平板)
 
隨後咱們一個個的把原來寫的layout文件找出來,找出裏面原來寫死的「數字」,好比寬度和字體大小之類的,通常來講單位是dp或者sp,將這些數字所有在values/dimens.xml中定義一個變量同時寫回layout文件中對應的數字的地方,這裏舉個例子:
原來的代碼是這樣的:
如今代碼成了這樣:
dimens.xml中添加的內容:
 
而後你將values-sw600dp-land的裏面的dimens.xml分別乘以0.75來得到:(由於600/800等於0.75)
 
values-sw480dp-land裏面的dimens.xml分別乘以0.6來得到(由於480/800等於0.6)
 
values-sw800dp-land保持和values裏面的同樣,由於它是base size。
這樣子之後咱們再運行代碼到 1920*1200分辨率320dpi的平板上,發現這個時候字體還有空間寬高都和原來的base size的如出一轍了,就像是原封不動的跑在base size平板上的感受!
 
那麼這個時候問題來了:
那麼多的layout文件夾自己每一個建立一個dimen變量就夠累了,而後還要分別拷貝一份同時手動計算乘以0.6或者0.75來得到新的值,拿計算器一個個的計算,多大的工做量啊,何況之後要是要來個sw102八、sw320呢?再次算一次?
 
這裏提供一個方法:
在代碼裏面新加一個帶main的java類,事實上它是自動的幫你在原來的values/dimens.xml的基礎上自動的幫你把每一個dimen乘以你所須要的乘積而後將結果寫回對應的dimens.xml裏面,這大大的減輕了工做量,之後每次修改dimens.xml只要這樣子便可:
  1. 在values/dimens.xml裏面添加或修改變量
  2. 跑一邊上述java類自動的生成別的dimens.xml
這裏給出這個工具java類的代碼:
public class DimenTool {

    public static void gen() {

        File file = new File("./app/src/main/res/values/dimens.xml");
        BufferedReader reader = null;
        StringBuilder sw480 = new StringBuilder();
        StringBuilder sw600 = new StringBuilder();
        StringBuilder sw720 = new StringBuilder();
        StringBuilder sw800 = new StringBuilder();
        StringBuilder w820 = new StringBuilder();


        try {
            System.out.println("生成不一樣分辨率:");
            reader = new BufferedReader(new FileReader(file));
            String tempString;
            int line = 1;
            // 一次讀入一行,直到讀入null爲文件結束

            while ((tempString = reader.readLine()) != null) {

                if (tempString.contains("</dimen>")) {
                    //tempString = tempString.replaceAll(" ", "");
                    String start = tempString.substring(0, tempString.indexOf(">") + 1);
                    String end = tempString.substring(tempString.lastIndexOf("<") - 2);
                    int num = Integer.valueOf(tempString.substring(tempString.indexOf(">") + 1, tempString.indexOf("</dimen>") - 2));

                    sw480.append(start).append((int) Math.round(num * 0.6)).append(end).append("\n");
                    sw600.append(start).append((int) Math.round(num * 0.75)).append(end).append("\n");
                    sw720.append(start).append((int) Math.round(num * 0.9)).append(end).append("\n");
                    sw800.append(tempString).append("\n");
                    w820.append(tempString).append("\n");

                } else {
                    sw480.append(tempString).append("\n");
                    sw600.append(tempString).append("\n");
                    sw720.append(tempString).append("\n");
                    sw800.append(tempString).append("\n");
                    w820.append(tempString).append("\n");
                }
                line++;
            }
            reader.close();
            System.out.println("<!--  sw480 -->");
            System.out.println(sw480);
            System.out.println("<!--  sw600 -->");
            System.out.println(sw600);

            System.out.println("<!--  sw720 -->");
            System.out.println(sw720);
            System.out.println("<!--  sw800 -->");
            System.out.println(sw800);

            String sw480file = "./app/src/main/res/values-sw480dp-land/dimens.xml";
            String sw600file = "./app/src/main/res/values-sw600dp-land/dimens.xml";
            String sw720file = "./app/src/main/res/values-sw720dp-land/dimens.xml";
            String sw800file = "./app/src/main/res/values-sw800dp-land/dimens.xml";
            String w820file = "./app/src/main/res/values-w820dp/dimens.xml";
            writeFile(sw480file, sw480.toString());
            writeFile(sw600file, sw600.toString());
            writeFile(sw720file, sw720.toString());
            writeFile(sw800file, sw800.toString());
            writeFile(w820file, w820.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    public static void writeFile(String file, String text) {
        PrintWriter out = null;
        try {
            out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            out.println(text);
        } catch (IOException e) {
            e.printStackTrace();
        }

        out.close();
    }

    public static void main(String[] args) {
        gen();
    }
}

 

圖片的適配


文字和空間寬度適配後,你們可能發現部分的ImageView或者ImageButton部分還有些變大或者變小,有的變模糊了,這裏須要美工提供多套圖片,你們請看這張圖:
這裏說明了開發時應該圖片以160dpi爲基準,同時提供不一樣dpi的基於baseline的圖片的放大或者縮小版本。那麼每套圖片放什麼地方呢?

以上是Google官方給出的分類標準( 雖然 Android 也支持低像素密度 (LDPI) 的屏幕,但無需爲此費神,系統會自動將 HDPI 尺寸的圖標縮小到 1/2 進行匹配
 
因此對於咱們的例子中 1920*1200分辨率320dpi的平板,咱們應該讓美工製做放大2x的版本的圖片,同時將圖片放到drawable-xhdpi文件夾中,原來的圖片放到drawable-mdpi文件夾中。這裏須要注意一下,對於drawable你可能會寫不少的好比shape或者selector的xml形式的drawable,他們自己不是圖片而是一個xml文件,可是他們都會去引用真實的drawable圖片,對於這種xml最好是要放到無dpi影響的drawable文件夾中(無後綴)
 
這樣子以來,咱們再把代碼跑 1920*1200分辨率320dpi的平板上,發現圖片部分也OK了,適配完畢。
 
這裏還須要提醒的一點,並非每一個地方的圖片都須要提供多套圖片這種方案來解決,由於這種方案會帶來使apk的size變大的反作用。因此你們能夠根據實際需求,若能夠經過上一節的方式來修改imageView的尺寸大小來解決(而非用wrap_content來指定layout_width和layout_height)的話,就不須要用多套圖片的方式。
 
 

應用啓動圖標的適配


對於高分辨率低dpi的設備,咱們常常會發如今launcher中咱們的應用的啓動icon被拉伸的模糊了,嚴重影響了門面的形象。
這裏咱們也經過提供多套icon的方式來解決,下面列表給出了不一樣屏幕密度中推薦的icon的size大小

 
在Android4.2以上的版本中,提供了對mipmaps的支持,說簡單點就是他能對bitmap進行縮放的時候減小一些性能的耗損。若是你用Andorid Studio開發Android程序會發現Android Studio自動幫你建立了幾個mipmaps文件夾,你可將應用的啓動圖標放到不一樣的mipmaps文件夾中而不是上述的drawable文件夾中,好比:

 
這裏你至少要提供一個xxxhdpi類型的啓動圖標,由於Android會幫你自動縮小圖標到對應的別的分辨率上(放大是會變模糊的),這樣子能夠節省些apk size。
相關文章
相關標籤/搜索