一、引言
Android的开源使厂商无需自行研发OS,大大降低了研发、生产的成本,使得Android平板品牌如雨后春笋般爆发,山寨机厂商们似乎又找到了一丝希望。与此同时带来的是广大开发者的苦不堪言,各种神奇的小板儿考验着app的兼容性,各种定制的rom不经意间就让app崩溃,光是界面上的调整就已经够你喝一壶了,是不?
二、适配可行性
早在Android设计之初就考虑到了这一点,为了让app适应标准or山寨屏幕,google已经有一套成熟的解决方案。其中,有这么几个指标需要注意:
(1)屏幕尺寸:单位inch,指的是屏幕对角线长度。
(2)屏幕密度:单位dpi,指的是每inch上可以显示多少像素点即px。
(3)屏幕分辨率:单位px * px,指的是一屏显示多少像素点。
(4)屏幕无关像素:单位dp/dip,指的是自适应屏幕密度的像素,用于指定控件宽高。
(5)刻度无关像素:单位sp,指的是自适应字体的像素,用于指定文字大小。
以我自己的Haier W910超级战舰(宽高比16:9)为例,上述单位的换算如下:
已知数据:屏幕尺寸4.5, 分辨率1280 * 720, 屏幕密度320
(1)16:9的4.5寸屏幕由勾股定理计算其高约为3.9寸,宽约为2.2寸
(2)则竖向dpi为1280 / 3.9 ≈ 328, 横向dpi为720 / 2.2 ≈ 327
(3)工业上切割液晶板时取整为320
那么既然dpi是自适应屏幕密度的,与px之间又是如何换算呢:
120dpi(ldpi低密度屏) 1dp = 0.75px (由于像素点是物理点,所以用2个像素点来显示3个dp的内容)
160dpi(mdpi中密度屏) 1dp = 1px
213dpi(tvdpi电视密度屏) 1dp = 1.33px
240dpi(hdpi高密度屏) 1dp = 1.5px
320dpi(xhdpi极高密度屏) 1dp = 2px
由上述分析结果可知,控件使用dp,文字使用sp即可满足自适应的需求。
三、适配方案
根据目前的调查,在市面上的平板,基本上属于mdpi和hdpi的,少数属于tvdpi(如google出的nexus7),所以我们选择这三种密度考虑适配;此外手机应用大多数都是竖屏使用,但平板作为娱乐性的一款产品,横竖屏均有使用的时候,所以我们还需要考虑到屏幕状态进行适配;最后考虑到有的rom会将虚拟键计算到屏幕尺寸里,还要考虑到虚拟键所占用的长宽。
那么如何根据这三个属性来进行适配呢?Android在资源文件values用文件名的方式提供了限定符可以帮助我们判断上述情况,限定符(mdpi,tvdpi,hdpi)可以帮助我们判断屏幕密度,限定符(land,port)可以帮助我们区分屏幕横竖屏状态,而限定符(1024x600...)可以适配计算虚拟键或者不计算虚拟键的屏幕,限定符的详细说明请参见Android SDK文档中开发者指南的Supporting Multiple Screens话题。
最终适配文件夹如下图所示:
注1:分辨率限定符的匹配是向下匹配,如果没有values-land-mdpi-1024x552,比如,分辨率values-land-mdpi-1024x600的屏幕,当rom不把虚拟键计算到屏幕尺寸时,实际显示的屏幕应该是values-land-mdpi-1024x552,无法适配到values-land-mdpi-1024x600,那这样就可能适配到下一级,比如values-land-mdpi-800x480,但是现在的平板已经没有这么低的分辨率了,所以是配到无限定符的values-mdpi里,造成界面显示上的瑕疵。
注2:由于分辨率限定符的匹配是向下匹配,所以如果有非主流mdpi屏幕不能精确适配到上述指定值时,values-mdpi至少可以保证app运行时不至于崩溃,同理values可以保证ldpi屏幕的平板不会因生成view而又取不到相应值而崩溃。
原帖地址:http://www.cnblogs.com/zealotrouge/archive/2012/11/23/2784774.html
附:dimens值按比例缩放工具:
package com.wscq;
import java.math.BigDecimal;
import java.util.Iterator;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class ChangeDimensValue {
public static void main(String[] args) throws Exception {
ReadAndChange();
}
private static void ReadAndChange() throws Exception {
String path = ChangeDimensValue.class.getResource("/dimens.xml").getPath();
SAXReader reader = new SAXReader();
// 读取xml文件内容,在内存中生成dom树
// Document对象doc是树根
Document doc = reader.read(path);
Element email = doc.getRootElement();// 获得根元素
// 测试迭代器遍历下层元素
iterator(email);
}
private static void iterator(Element email) {
StringBuffer sb = new StringBuffer();
// e.elementIterator()获得下层元素迭代器
Iterator it1 = email.elementIterator();
sb.append("");
sb.append("");
while (it1.hasNext()) {
Element e = it1.next();
sb.append("<" + e.getName());
Iterator itt = e.attributeIterator();
Attribute att = itt.next();
sb.append(" " + att.getName() + "=" + att.getValue());
sb.append(">");
// 此处的倍数为缩放倍数,这里是720*1280变换为480*800
sb.append(toMULNumber(e.getText(), new BigDecimal("0.82")));
sb.append("" + e.getName() + ">");
}
sb.append(" ");
System.out.println(sb.toString());
}
private static String toMULNumber(String num, BigDecimal d) {
BigDecimal bigDecimal = new BigDecimal(num.substring(0,
num.length() - 2));
bigDecimal = bigDecimal.multiply(d);
int number = bigDecimal.intValue();
return number + num.substring(num.length() - 2, num.length());
}
}
使用时后,需要把dimens放入src目录下,直接运行,既可以在控制台输出。