关于Android不同密度下的图片资源

322 阅读3分钟

这篇文章主要是对关于Android的资源加载 进行简单的补充。

  • 不同密度下图片资源

为了适配多款手机,我们一般会在多个文件夹中,存放同名的图片。

drawable
drawable-mdpi
drawable-hdpi
drawable-xhdpi
drawable-xxhdpi
drawable-xxxhdpi
...

这些文件夹有什么不同?前面有聊过,资源内容加载后会把加载到的相关数据,存放到TypeValue,其中type主要代表文件内容的类型,data代表内容数据,它还有一个属性density。通过一个简单的例子,可以看到这个属性的作用。

  1. 把一张图片只存放到drawable目录

  解析出来的结果typeValue.density = 160

  1. 把一张图片只存放到drawable-mdpi目录

  解析出来的结果typeValue.density = 160

  1. 把一张图片只存放到drawable-hdpi目录

  解析出来的结果typeValue.density = 240

  1. 把一张图片只存放到drawable-xhdpi目录

  解析出来的结果typeValue.density = 320

  1. 把一张图片只存放到drawable-xhdpi目录

  解析出来的结果typeValue.density = 480

  1. 把一张图片只存放到drawable-xhdpi目录

  解析出来的结果typeValue.density = 640

所以,可以推断出,这几个目录对应的资源密度

drawable:160
drawable-mdpi:160(1)
drawable-hdpi:240(1.5)
drawable-xhdpi:320(2)
drawable-xxhdpi:480(3)
drawable-xxxhdpi:640(4)
...

但是如果我把图片放在多个目录呢,会读取哪个目录的图片?根据什么规则?继续来看一个例子,

这个例子是在一部手机密度为density=440情况下进行的。通过adb命令可以查看当前手机密度。

adb shell wm density

把三张同名图片分别放在下面目录

drawable-mdpi:160(1)
drawable-xxhdpi:480(3)
drawable-xxxhdpi:640(4)

最后解析出来的数据typeValue.density = 480

把二张同名图片分别放在下面目录

drawable-mdpi:160(1)
drawable-xxxhdpi:640(4)

最后解析出来的数据typeValue.density = 640

所以推测,native会解析离当前密度440最接近的资源。

当前密度是440,第一个例子距离最近的是480,第二个例子最近的是640

  • 图片缩小

有时候,我们发现加载到的bitmap图片大小跟所有目录下的图片大小都不一样。

如果把图片只放在drawable-xxxhdpi,那它最后肯定只能读取这个资源,不管手机密度多少。

但是读取到的资源,它会有一个缩放处理。因为当读取的资源密度大于当前手机密度的时候,需要进行缩小。

这段逻辑主要来自ImageDecoder图片解码器。

private int computeDensity(@NonNull Source src) {
    if (this.requestedResize()) {
        return Bitmap.DENSITY_NONE;
    }

    final int srcDensity = src.getDensity();
    if (srcDensity == Bitmap.DENSITY_NONE) {
        return srcDensity;
    }

    if (mIsNinePatch && mPostProcessor == null) {
        return srcDensity;
    }

    Resources res = src.getResources();
    if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
        return srcDensity;
    }

    final int dstDensity = src.computeDstDensity();
    if (srcDensity == dstDensity) {
        return srcDensity;
    }

    //密度对比
    if (srcDensity < dstDensity
            && Compatibility.getTargetSdkVersion() >= Build.VERSION_CODES.P) {
        return srcDensity;
    }

    //缩放处理
    float scale = (float) dstDensity / srcDensity;
    int scaledWidth = Math.max((int) (mWidth * scale + 0.5f), 1);
    int scaledHeight = Math.max((int) (mHeight * scale + 0.5f), 1);
    this.setTargetSize(scaledWidth, scaledHeight);
    return dstDensity;
}