Android多屏幕适配学习笔记

来源:互联网 发布:java的tostring方法 编辑:程序博客网 时间:2024/06/05 12:04
  我所在公司的产品以硬件为主,软件跟着特定的硬件平台走,所以,虽然从事着Android开发,却从来没有处理过多屏幕适配的问题。意识到这个可能成为自己的一个短板,所以在缺乏实践的情况下,先准备点理论知识,聊以慰藉。

     1 常用单位
      px:pixel //像素
      in: inch //英寸
      mm : 毫米
      pt : 1/72英寸
      dpi : dots peer inch //一英寸长的直线上的像素点的数量,即像素的密度
      dp(dip) : Density-independent pixel //密度无关像素,定义为160dpi的屏幕下,1个像素点的长度
      sp : Scale-independent Pixels //专用于设定文字大小,和dp类似,但是它除了受到dpi影响,还受到用户的字体偏好设定影响
      屏幕的物理尺寸:屏幕对角线的长度,以inch为单位
      分辨率: 屏幕上长宽方向上像素点的数量, 例A80为全高清屏,即1080*1920的分辨率// 也称为相对分辨率(使用这种说法的人认为,dpi才是真正的分辨率)

      android.util.TypedValue类提供了一个函数,支持把所有的单位换算到px,实现代码如下:
<span style="font-size:14px;"><span style="font-size:18px;">    /**     * Converts an unpacked complex data value holding a dimension to its final floating      * point value. The two parameters <var>unit</var> and <var>value</var>     * are as in {@link #TYPE_DIMENSION}.     *       * @param unit The unit to convert from.     * @param value The value to apply the unit to.     * @param metrics Current display metrics to use in the conversion --      *                supplies display density and scaling information.     *      * @return The complex floating point value multiplied by the appropriate      * metrics depending on its unit.      */    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;        case COMPLEX_UNIT_SP:            return value * metrics.scaledDensity;        case COMPLEX_UNIT_PT:            return value * metrics.xdpi * (1.0f/72);        case COMPLEX_UNIT_IN:            return value * metrics.xdpi;        case COMPLEX_UNIT_MM:            return value * metrics.xdpi * (1.0f/25.4f);        }        return 0;    }</span></span>



      2 市占率比较高的手机
      这些数据可以在http://screensiz.es/获得:

手机型号物理尺寸(inch)分辨率(px)dpipx密度Galaxy SIII4.8720*1080360XHDPISony Xperia Z51080*1920441XXHDPIGalaxy SIII4.3480*800219HDPIGalaxy Nexus4.6720*1280316XHDPIGoogle Nexus 44.7768*1280320XHDPIGalaxy SIV51080*1920441XXHDPIGalaxy S Plus4480*800233HDPIGalaxy Note II5.5720*1280267XHDPIGalaxy S4480*800233MDPIGalaxy Note5.3800*1280285XHDPI

从数据来看,目前大部分的手机的屏幕尺寸在4~5英寸之间(抛开note之类的跨界产品),DPI在HPI到XXHDPI(基本只有几款旗舰机才会配置)之间。

    3 dp单位的使用
    dp单位的显示效果与dpi无关,那它实际的使用效果如何呢?

     我创建了一个Nexus S的模拟器,参数为:Android2.3.3,屏幕4寸,解析度480*800,HDPI。
然后准备了如下layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity">    <LinearLayout        android:layout_height="100dp"        android:layout_width="100dp"        android:background="#FF0000"/></RelativeLayout>

执行,获得如下结果:


计算得到LinearLayout的实际大小为:150*150。
所以,dp适配到具体设备的像素的时候,是通过如下公式换算:
px = dp * scale;
scale由设备的由设备的设备的pixel密度决定:
LDPI的dpi在120左右,scale = 0.75 //一般市面上是看不到LDPI的设备了。
MDPI的dpi在160左右,scale = 1.0
HDPI的dpi在240左右,scale = 1.5
XHDPI的dpi在320左右,scale = 2.0
XXHDPI的dpi在440左右,scale = 3.0
所以,实际使用中,1dp约等于1/160inch,所以,可以让我们的app在大多数的屏幕上保持相近的呈现效果。

但是,使用dp来适配所有设备是不现实的:
1. 呈现效果仍旧会有差异,仅仅是相近而已。
2. 当设备的物理尺寸存在差异的时候,dp就显得无能为力了。为4.3寸屏幕准备的UI,运行在5.0寸的屏幕上,很可能在右侧和下侧存在大量的空白。而5.0寸的UI运行到4.3寸的设备上,很可能显示不下。

4. 根据属性的资源适配机制

如果想获得最好的适配效果,那么根据不同平台的参数进行针对性的适配,毫无疑问可以获得最佳的效果。系统会根据设备的具体参数,根据资源文件的属性筛选最合适的资源。
apk中提供的资源包括animator、anim、color、drawable、layout、menu、raw、values、xml这9种。这些资源保存在res文件下的对应文件夹中。
每个文件夹的名字都可以用多种属性来修饰,以便系统在执行的过程中根据设备的实际情况使用不同的资源。
    
    不同的属性以“-”连接,以优先级从高到低的方式排列(否则编译错误),形式如下:
资源名-属性1-属性2-属性3...
 

以下是几种个人觉得比较有用的几个属性:
Language and region : 用于指定语言和区域,多国语言最常用的属性,例如:en,en-rUS。
Screan size : 用来指定屏幕大小,包含如下值(由小到达):small(最小尺寸426*320dp,基本在3.2寸以上),normal(最小尺寸470*320dp,基本在3.3寸以上),large(最小尺寸640*480dp,基本在5.0寸以上),xlarge(最小尺寸960*720,基本在7.5寸以上)。
Screan orientation : 屏幕方向,值为port或者land。
UI mode : 值为car、desk、television、appliance。
Night mode : 值为ienight、 notnight(可能对文本阅读类型的app比较有用)
Screan pixel density : 最常用的属性了,值包括:ldpi,mdpi,hdpi,xhdpi,xxhdpi,nodpi,tvdpi。其中,nodpi用于开发者不希望系统对图片进行缩放的情况,tvdpi的界于hdpi和mdpi之间,dpi一般在213左右,多用于基于Android系统的智能电视,大部分app其实很少用到。
Platform version : 值为:v<version number>,例如v15属性修饰的资源仅用于4.0及以上的设备。
Available width : 值为w<number>dp, 例如适配640dp宽度的设备:w640dp
Aailable height : 值为h<number>dp, 例如适配320dp高度的设备:h320dp
另外,还有一种指定分辨率的属性(google的官方资料中没有提到这种方式,但是很多开发者都使用了),用法为long side x short size,例如适配Nexus S(480x800)的资源可以如下方式指定800x480。但是,使用这种方式请注意:某些手机没有实体按键,而是采用了虚拟按键的设计。这种情况下,app可以使用的尺寸会小于设备的屏幕大小,例如Galaxy Nexus的分辨率为1280x720,但是实际上它只能适配1190x720或者1280x630左右大小的资源。

5. 资源缺失情况下的适配
   app运行时,系统会根据属性选择适配的资源进行展示。如果有符合的资源则使用,反之,当符合的资源不存在时,系统会去寻找最相近的可用资源来代替。但是,查找的属性不同,查找的顺序会有所有差异。

对dpi属性来说,查找的顺序为,高dpi的资源优先。例如,没能找到hdpi的图片资源,则系统的搜索顺序是drawable-xhdpi->drawable-xxhdpi->drawable-mdip->drawable->drawable->drawable-ldpi。这里drawable被认为比drawable-ldpi更接近hdpi。
   另外,对于图片资源在找到相近的资源后,系统还需要对图片进行缩放才会进行使用(否则,可能出现显示不下,或者图片过小的问题)。上文的例子中,如果在drawable-xhdpi中找到了资源,那么找到的图片会先缩小到0.75倍以后再使用;如果是在drawable-mdpi中找到了资源,那么图片需要放大到1.5倍以后再使用(放大肯定会造成图片模糊,可能这就是高dpi资源优先的原因所在)。
有时候,我们的图片资源不一定是从drawable文件夹中读取的,还有可能是从sd卡上读取的,或者从网络上下载的。这个时候,我们需要注意,默认情况下,通过BitmapFactory.decodeFile()函数生成的图片被认为是MDPI的,如果想让图片也获得与drawable文件夹相似的缩放能力,则需要通过BitmapFactory.Option.inDensity属性设置(例如如果图片是为hdpi准备的,则设置为240)。

对于screan size,查找的顺序则是小尺寸优先,大尺寸放弃。例如,在Galaxy note 2上执行apk时,如果未能找到layout-large资源,则查找顺序为:layout-normal->layout->layout-small,不会查找layout-xlarge。
   6. 个人对于多设备UI适配的理解
以下是个人的一点总结:
I. 使用简洁的风格来设计UI,让界面变得简单并且一体化,使UI有更加的自适应能力。
II. 尽量使用match_parent,wrap_content等属性来实现实现UI的自适应,减少dp的使用,尽量不要使用px。
III. 如果使用dp,那么不要在layout文件中显示的设定数值,而是通过dimens文件来引用,不同设备上就可以使用同一份layout,而通过不同的dimens来适配。
IV. 可以的情况下,尽量使用.9的png文件,通过无损的缩放来适应UI。
V. 有些在XML上很难设定的UI细节,可能可以通过java代码动态调整的方案来解决。
VI. 如果有需要的话,可以通过w720dp,h360dp,1024x768等属性来对市面上销量比较好的手机做针对性的UI适配。
VII. 没有真机的情况下,可以通过SDK内的模拟器和网上的在线模拟器检查UI效果。
VIII.对于某些app,可以使用Html5来开发UI(即以app内嵌WebView控件来展示Html5),可能可以获得更加的UI适应效果。

最后记录几个与UI相关的JAVA API:
DisplayMetrics dm = Context.getResource().getDisplayMetrics()//dm中可以获得scaledDensity,densityDpi,heightPixels,widthPixels等信息。

Configuration config = getResources().getConfiguration() //config中可以获取设备的配置信息,我们可以根据具体的信息展现不同的UI。这里有一点值得注意的是,config.screenWidthDp和理论计算可能会有偏差:例如,9100的参数为800x480,hdpi,800/1.5=533,但是实际运行时,config.screenWidthDp=508。如果为9100做UI适配,那么“w508dp”才是有效的,”w533dp“则无法匹配给9100。相似的,偏差也可能出现在config.screenHeightDp上。


参考资料:
Google官方资料
Android屏幕适配-资源文件夹命名与匹配规则
Android开发笔记-屏幕适配
Android多屏幕适配
0 0