在项目中,之前使用的是分辨率为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文件夹下,即可适配不同分辨率屏幕。
利用上述方法可以比较好的解决我这次需要适配的两个屏幕。