这篇文章主要是对关于Android的资源加载 进行简单的补充。
- 不同密度下图片资源
为了适配多款手机,我们一般会在多个文件夹中,存放同名的图片。
drawable
drawable-mdpi
drawable-hdpi
drawable-xhdpi
drawable-xxhdpi
drawable-xxxhdpi
...
这些文件夹有什么不同?前面有聊过,资源内容加载后会把加载到的相关数据,存放到TypeValue
,其中type
主要代表文件内容的类型,data
代表内容数据,它还有一个属性density
。通过一个简单的例子,可以看到这个属性的作用。
- 把一张图片只存放到
drawable
目录
解析出来的结果typeValue.density = 160
- 把一张图片只存放到
drawable-mdpi
目录
解析出来的结果typeValue.density = 160
- 把一张图片只存放到
drawable-hdpi
目录
解析出来的结果typeValue.density = 240
- 把一张图片只存放到
drawable-xhdpi
目录
解析出来的结果typeValue.density = 320
- 把一张图片只存放到
drawable-xhdpi
目录
解析出来的结果typeValue.density = 480
- 把一张图片只存放到
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;
}