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資料夾下,即可適配不同解析度螢幕。
利用上述方法可以比較好的解決我這次需要適配的兩個螢幕。