安卓现在的主要版本是2.2/2.3,以及4.0以上,从下图可以看出,可以从API 10以上开始支持,甚至激进些,直接从API 15开始支持。或者说再过半年,可以只考虑4.x了,现在可能还不行。
分辨率更是百花齐放了!下面是某App的屏幕分辨率一个月的分布,可以看到xhdpi甚至xxhdpi占主流,FULL HD的设备也越来越多,不过对于平板,基本上就只是Nexus 7和三星某几款平板,别的市场占有率实在是不行,如果精力不够,可以只适配手机。
结论是也许到2014年,就可以都只适配4.x,但是现在兼容2.3是比较安全的策略。而高分屏是大趋势,所以要特别为大屏高分手机做优化,因为都是“高端”用户。
从上图可以看到,除了屏幕本身尺寸外,屏幕像素密度(DPI)也是一个维度来区别具体设备的,而且在适配中更常用。
上图是具体的分辨率,尺寸,DPI的分布。
首先在manifest里面声明,你的应用想要支持的屏幕尺寸和DPI的种类,比如下面的来者不拒的例子:
<supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:xlargeScreens="true"/>
对于不同的DPI需要提供不同的图片资源,分别放在以下目录。对设计师而言,这需要额外的工作量,但是有工具可以简化这件事情,在下一章定制UI里,我们会讲到。
res/drawable //默认资源 res/drawable-ldpi res/drawable-mdpi res/drawable-hdpi res/drawable-xhdpi res/drawable-xxhdpi
在安卓上View的宽高采用的单位推荐使用DIP(Density Independent Pixel),也叫做dp。dp是一个相对的值,以上面的mdpi为基准,1dp对应1px,而hdpi是1.5像素,xhdpi是2px,ldpi是0.75px。
与dp不同,px是真正的物理像素,比如在hdpi上的设备上1px是0.1毫米,在xhdpi可能是0.05毫米(只是打比方,数据不准确)。除了一些精确要求像素值的地方使用px,比如分割线等,其他的长度单位推荐用dp。
px = dp * (dpi / 160)
sp(scale-independent pixel),也是相对的单位,主要用于设置字体大小。和dp类似,基本也符合上面的公式,但是还受用户的设置里面的字体大小设定值scale影响,如下
px = sp * (dpi / 160) * scale
所以使用sp时,当用户在系统设置时更改了字体大小,你的应用就能做到对应的变化。
如果对换算有兴趣的同学可以看下面的安卓源码,源码可以在你的SDK目录里面找到,类似sources/android-17之类的路径下面:
//android.util.TypedValue里的转换不同单位的函数 public static float applyDimension(int unit, float value, DisplayMetrics metrics) { switch (unit) { case COMPLEX_UNIT_PX: return value; case COMPLEX_UNIT_DIP: return value * metrics.density; //具体看DisplayMetrics的注释 case COMPLEX_UNIT_SP: return value * metrics.scaledDensity; //具体看DisplayMetrics的注释 ... } return 0; }
除了尺寸使用dp来适配不同屏幕宽度,合理使用wrap_content和match_parent之外,LinearLayout的weight属性,也是常用来适配的一个手段。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <View android:layout_width="match_parent" android:layout_height="wrap_content" /> <View android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1.0" /> </LinearLayout>
上面的第二个View设置了weight,那么会自动占满除了第一个View的剩余的屏幕空间。如果两个View都设置了weight的值,而且都大于零,那么它们所占的比例和weight值是成反比的。比如weight分布为1.0和2.0,那么2个View的高度,实际上是2/3和1/3个屏幕高度的。
很多页面横屏直接拉伸也是可以接受的,当然某些应用的场景还是需要做到精致些的。最简单的适配横屏是直接换一个横屏的layout,放到res/layout-land,或者使用同一个layout,但是使用不同的style,放到res/values-land/style.xml。
这里注意一个细节,Activity在屏幕旋转时,默认是直接onStop -> onRestart了,需要在manifest里设置下面的属性:
android:configChanges="orientation|keyboardHidden|locale|screenSize"
然后在Activity的 onConfigurationChanged 方法里,做具体处理,比如判断config当前是不是横屏,对控件做微调等等。
安卓的各种资源可以使用后缀,系统会根据当前设备的情况来做从最精确到最宽泛的匹配,拿layout为例,可以从下面几个维度来做后缀:
res/layout-xlarge-tvdpi-land //顺序是尺寸,DPI,方向,当然这是个极端的例子 res/layout-tvdpi //Nexus 7的诡异DPI res/layout-land //横屏 //Android 3.2引入的新的后缀,主要服务于平板 res/layout-sw600dp //smallest possible width,适配最小宽或者高是600dp的设备 res/layout-w720dp //available screen width,适配最小宽度是600dp的设备 res/layout-h720dp //available screen height,适配最小高度是600dp的设备
更多关于适配,可以仔细参考screens support。
我个人认为安卓应用的设计风格首先要尽量遵守Holo风格,当然如果能有打破Holo风格的设计那就更加好了,但是一定要在理解的基础上。照抄iOS的设计,开发出来的应用,一是开发成本大,二是没有用到安卓的系统优势和系统风格出入太大,比如微信。
想要开发一款精美的应用,不光要靠设计师的努力,也要靠开发工程师的精雕细琢。UI定制,主要有下面几种途径。
前面两条都是通过使用style或者theme来覆盖默认控件的样式,当然也可以在代码里动态指定。第3条,一般是通过继承某个View控件,复写onDraw方法或者其他监听事件的回调。从头自己定义控件,一般不多见,除非有足够强的理由。
因为大部分应用都使用ActionBar作为导航方式,这里推荐Android Asset Studio,可以方便的定制ActionBar相关的样式,并生成打包对应的style和icon等资源文件,大大的减少了设计师和开发者的工作量。