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


問題的引入


 

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

 
屏幕適配主要從圖片和 距離(文字) 進行下手。從以往的方式適配方式中,開發者可能會考慮各種各樣的分辨率,比如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來獲得新的值,拿計算器一個個的計算,多大的工作量啊,況且以后要是要來個sw1028、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。
 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM