1. 程式人生 > >Android 軟體螢幕適配

Android 軟體螢幕適配

在專案中,之前使用的是解析度為1280*800的7寸螢幕,由於業務需要,現在更換為1024*600解析度的7寸屏。更換完成後,由於之前的Android軟體沒有做螢幕的適配,從而導致軟體介面顯示不完整。因此需要進行螢幕適配。 此次適配需要達到的目標是在這兩種解析度下,顯示的介面相同。

1. 基礎知識

在進行螢幕適配之前,先要了解一下與螢幕有關的幾個概念:螢幕尺寸、螢幕解析度、畫素密度。

1.1 螢幕尺寸

螢幕尺寸指的是螢幕的對角線長度,單位一般使用英寸(inch)表示。1英寸=2.54釐米。

1.2 螢幕解析度

螢幕解析度指的是螢幕上面畫素的的總和,單位一般採用px(pixel),通常使用橫向上的畫素數*豎向上的畫素數表示,如上面的1280*800,表示橫向上有1280個畫素,豎向上有800個畫素,總共有1280*800個畫素,即螢幕的解析度為1280*800。

1.3 畫素密度

畫素密度指的是每英寸上有幾個畫素點,單位採用dpi(dots per inch)。由於我們一般都知道螢幕尺寸和解析度,因此,採用對角線上的畫素點數除以螢幕尺寸得到相應螢幕的畫素密度。

根據畫素密度不同,可以將裝置分為以下幾種型別:

畫素密度(dpi) 密度型別
120 低密度(ldpi)
160 中密度(mdpi)
240 高密度(hdpi)
320 超高密度(xhdpi)
480 超超高密度(xxhdpi)

1.4 密度無關畫素

密度無關畫素是Android中特有的一個概念,單位是dp。它的作用是以160dpi螢幕為基準,根據不同的畫素密度,1dp代表不同的畫素數量,如下表所示。

畫素密度 密度型別 換算關係
120 低密度(ldpi) 1dp=0.75px
160 中密度(mdpi) 1dp=1px
240 高密度(hdpi) 1dp=1.5px
320 超高密度(xhdpi) 1dp=2px
480 超超高密度(xxhdpi) 1dp=3px

2. 螢幕適配思路

參考張鴻洋的部落格,他裡面提到了一種利用百分比的方式進行適配。

其基本思路是:

  • 首先選取一種解析度作為基準,如480*320

  • 然後其他解析度的螢幕都以該解析度為基準,進行長寬的比例換算,部落格中使用800*480比例進行換算,得到兩個檔案,部分內容如下:

      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <dimen name="x1">1.5px</dimen>
          <dimen name="x2">3px</dimen>
          <dimen name="x3">4.5px</dimen>
          <dimen name="x4">6px</dimen>
          ...
          ...
    
  • 接著在編寫佈局檔案時,使用長度採用

      @dimen/x160
      @dimen/y50
    

使用這種方法後,我們相當於只要針對了基準解析度的螢幕進行了介面繪製。當採用不同解析度後,會呼叫對應的檔案,將之前繪製的介面進行縮放,從而實現各個控制元件所佔的百分比相同。

3. 適配檔案生成

上面說到基於基準解析度需要編寫待適配的換算檔案。每個檔案都有幾百行,一個一個檔案手動編寫顯然是不科學的,誰也不願意幹。上文提到的部落格中有提供生成該檔案的原始碼。下面是我自己編寫的一段簡單生成相關檔案的程式碼。

建立一個GeneralScreenAdapterFile類,其中的方法如下:

public class GeneralScreenAdapterFile {

        private int[] needAdapterX;
        private int[] needAdapterY;
        private int baseX=480;
        private int baseY=320;
        
        private final String xFileName="x_size_file.xml";
        private final String yFileName="y_size_file.xml";
        private final String rootDirectory="adapterFile/";
        
        /**
        * 設定基準解析度,預設基準解析度為480*320
        * @param baseX 橫向
        * @param baseY 縱向
        */
        public void setBaseAdapter(int baseX, int baseY) {
                if(baseX >0 && baseY>0) {
                        this.baseX=baseX;
                        this.baseY=baseY;
                }
        }
        
        /**
        * 將需要生產的解析度匯入,解析度採用字串形式,利用*分割,格式如:480*320
        * @param needAdapterResolution 需要生成的檔案的解析度
        * @return true 匯入成功;false 解析度有誤
        */
        public boolean setResolution(String[] needAdapterResolution) {
                needAdapterX=null;
                needAdapterY=null;
                if(needAdapterResolution == null) {
                        return false;
                }
                int length=needAdapterResolution.length;
                if(length != 0) {
                        needAdapterX=new int[length];
                        needAdapterY=new int[length];
                        for(int i=0;i<length;i++) {
                                String tempResolution=needAdapterResolution[i];
                                String[] temp=tempResolution.split("\\*");
                                if(temp.length != 2) {
                                        return false;
                                }
                                try {
                                        needAdapterX[i]=Integer.parseInt(temp[0]);
                                        needAdapterY[i]=Integer.parseInt(temp[1]);
                                }catch (Exception e) {
                                        // TODO: handle exception
                                        e.printStackTrace();
                                        return false;
                                }
                        }
                }
                return true;
        }

        /**
        * 生成對應的適配檔案
        * @return true生成成功;false生成失敗
        */
        public boolean generateAdapterFiles() {
                if(needAdapterX == null || needAdapterY == null) {
                        return false;
                }
                int length=needAdapterX.length;
                //生成基準解析度適配檔案
                createFile(baseX, baseY);
                //生成其他解析度適配檔案
                for(int i=0;i<length;i++) {
                        if(!createFile(needAdapterX[i], needAdapterY[i]))
                                return false;
                }
                return true;
        }
        
        /**
        * 生成指定解析度的適配檔案
        * @param x 橫向解析度
        * @param y 縱向解析度
        * @return true生成成功;false生成失敗
        */
        private boolean createFile(int x, int y) {
                //生成解析度對應的資料夾
                String directoryName="values-"+x+"x"+y+"\\";
                File directoryFile=new File(rootDirectory+directoryName);
                if(!directoryFile.exists()) {
                        directoryFile.mkdirs();
                }
                //生成X適配檔案
                try {
                        float factorX=(float)x/baseX;
                        File nowXFile=new File(rootDirectory+directoryName+"/"+xFileName);
                        FileWriter xFileWriter=new FileWriter(nowXFile);
                        xFileWriter.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
                        xFileWriter.write("<resources>\n");
                        xFileWriter.flush();
                        for(int j=1;j<=baseX;j++) {
//				float tempValue=(int)(factorX*j*100)/100f;//保留兩位小數
                                float tempValue=(int)(factorX*j+0.5);//四捨五入
                                xFileWriter.write("\t<dimen name=\"x"+j+"\">"+tempValue+"px</dimen>\n");
                                xFileWriter.flush();
                        }
                        xFileWriter.write("</resources>\n");
                        xFileWriter.flush();
                        xFileWriter.close();
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        return false;
                }
                
                //生成Y適配檔案
                try {
                        float factorY=(float)y/baseY;
                        File nowYFile=new File(rootDirectory+directoryName+"/"+yFileName);
                        FileWriter yFileWriter=new FileWriter(nowYFile);
                        yFileWriter.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
                        yFileWriter.write("<resources>\n");
                        yFileWriter.flush();
                        for(int j=1;j<=baseY;j++) {
//				float tempValue=(int)(factorY*j*100)/100f;
                                float tempValue=(int)(factorY*j+0.5);
                                yFileWriter.write("\t<dimen name=\"y"+j+"\">"+tempValue+"px</dimen>\n");
                                yFileWriter.flush();
                        }
                        yFileWriter.write("</resources>\n");
                        yFileWriter.flush();
                        yFileWriter.close();
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        return false;
                }	
                return true;
        }
}

使用過程中,首先使用setResolution()方法設定待生成的解析度;如果需要的話可以只用setBaseAdapter()方法設定基準解析度(預設解析度為480*320);最後使用generateAdapterFiles()方法生成對應的適配檔案。

public static void main(String[] args) {
	GeneralScreenAdapterFile generalScreenAdapterFile=new eneralScreenAdapterFile();
	String[] needAdapterResolution=new String[]{"1280*800", "1024*600"};//設定待生成的解析度陣列,使用*分割
	if(generalScreenAdapterFile.setResolution(needAdapterResolution)) {
		generalScreenAdapterFile.generateAdapterFiles();
	}
}

通過以上方法,在工程的更目錄下會出現“adapterFile”資料夾,該資料夾中根據解析度不同會出現不同的子資料夾,例如:values-1280x800,values-1024*600等,這些子資料夾中就是需要的適配檔案。將這些子資料夾拷貝到Android程式的res資料夾下,即可適配不同解析度螢幕。

利用上述方法可以比較好的解決我這次需要適配的兩個螢幕。