Android图片适配原理

1,一段常见的代码

关于Android中的大小尺寸,我们可能最常接触的概念就是spdippx。而最常使用的代码就是px与dip二者的相互转化,以及px与sp之间的相互转化,如下:

/**
     * 将px值转换为dip或dp值,保证尺寸大小不变
     *
     * @param pxValue
     * @param scale
     *            (DisplayMetrics类中属性density)
     * @return
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    /**
     * 将dip或dp值转换为px值,保证尺寸大小不变
     *
     * @param dipValue
     * @param scale
     *            (DisplayMetrics类中属性density)
     * @return
     */
    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    /**
     * 将px值转换为sp值,保证文字大小不变
     *
     * @param pxValue
     * @param fontScale
     *            (DisplayMetrics类中属性scaledDensity)
     * @return
     */
    public static int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }

    /**
     * 将sp值转换为px值,保证文字大小不变
     *
     * @param spValue
     * @param fontScale
     *            (DisplayMetrics类中属性scaledDensity)
     * @return
     */
    public static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }  

对于大多对android开发人员都很熟悉上面的代码,如果你不认识,那也没关系,接下来从各个基础概念讲起。

2,px,dip的关系

px,像素,英文简称pixel,简写自picture element,像很多文章中所说,像素就是图片中的一个点。

dip ,全称Density-independent Pixels。只要在布局中文件使用到大小,距离,官方都推荐使用dip(除了字体大小),一般我们对dipdp不做区别。dip这个单位是为了使同样数量的px在不同手机上的显示效果相同,显然1 dip1 px通过某种对应关系转化过来的。这种转化上面的代码已经有了,但是其原理是什么呢?这就得引出另一个概念dpi(dots per inch)

dpi,官方文档对这个单位的指代是Screen density,即屏幕密度,表示一个物理单位面积内像素点的个数。而在160dpi的设备上,1dip = 1px,在120dpi的设备上,1dip = 0.75px,没错,这就是pxdip对应关系的基础,dpi越大,1dip内就有更多的px

dpi类别 dpi大小 px dp 物理显示大小
ldpi 120dpi 120px 160dp 1个单位
mdpi 160dpi 160px 160dp 1个单位
hdpi 240dpi 240px 160dp 1个单位
xhdpi 320dpi 320px 160dp 1个单位
xxhdpi 480dpi 480px 160dp 1个单位

从上面这个表可以看出,在不同dpi的手机上,相同的的dp可以对应相应的物理大小,从而发挥了适配不同设备的作用。

从上面的对应关系,不难得出如下公式

px = dp * dpi / 160

那么再来看一下上面dppx的转化代码。

 /**
 * 将dip或dp值转换为px值,保证尺寸大小不变
 *
 * @param dipValue
 * @param scale
 *            (DisplayMetrics类中属性density)
 * @return
 */
public static int dip2px(Context context, float dipValue) {
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dipValue * scale + 0.5f);
}

context.getResources().getDisplayMetrics()返回的是一个DisplayMetrics对象,该对象有这么三个成员变量

public float density;

public int densityDpi;

public float scaledDensity;

densityDpi就是上面的dpi,density是当前设备和默认dpi = 160的比值,比如,对于dpi = 320的设备,densityDpi = 320,density = 2scaledDensity是和sp有关的参数,下面再讲。

再回头仔细看这段用于单位转化的代码,而刚刚得出的公式是不是一致的,没错,就是一样的,只是多了一个0.5,这个是为了四舍五入而已。到此为止,应该就理解了pxdp之间的关系了。

于是乎,应该也能理解android项目中的图片资源文件,一般我们需要在以下几个文件夹中提供不同大小的图片。

  • xhdpi: 2.0
  • hdpi: 1.5
  • mdpi: 1.0 (baseline)
  • ldpi: 0.75

如果你想要提供一张大小为200x200的照片给xhdpi的设备, 那相对应的就得提供一张150150的照片给hdpi的设备,提供100100mdpi,提供7575ldpi*。

再谈一个问题。大家一般都将图片文件按如下结构放在项目中。

search screenshot
资源图片文件

但是在最新的ADT中新建会见到下面这种结构

search screenshot
资源图片文件

其实很简单,mipmap是用来放置appicon图标的。其他资源图片文件也可以放在这里面,但是最好还是新建一个drawable的结构。真相在这个链接

http://developer.android.com/training/multiscreen/screendensities.html

3. sp

sp的全称Scale-independent Pixels,布局文件中对TextView指定字体大小时,都建议使用sp.spdip相比多考虑了手机字体大小的偏好设置。而sppx的转化关系,与dppx的转化关系,只是对应的比例参数从density变成了scaledDensity原理是一样的。

4。举例

这里举个特例,加深理解。

如果一个hdpi设备,在drawable-hdpi文件夹中没图片,反而在drawable-xhdpi文件夹中存在,那么加载到内存的图片会相比在drawable-hdpi的图片会变小一半,占用内存也会变小(变成四分之一),

另外,再注意一点,一个ImageView控件,修改其layout_heightlayout_width的值,图片显示大小会变化,但是像素,占用内存不会变化。

下面是我写的一个小demo的截图,可以参考参考,加深理解:

search screenshot

这是一个hdpi的设备,第一幅图加载hdpi的图片,第二幅图加载xhdpi的图片,第三幅图加载xhdpi图片但是指定了长宽。

此处getWidthgetHeigth返回的是图片长宽,单位是像素,单位是px,而非dip

4,参考链接

http://developer.android.com/guide/practices/screens_support.html

http://developer.android.com/training/multiscreen/screendensities.html

http://www.cnblogs.com/yaozhongxiao/archive/2014/07/14/3842908.html

http://stackoverflow.com/a/2025541/1290235