前言
文章基本抄袭郭霖大神的Android drawable微技巧,你所不知道的drawable的那些细节,略做修改,为了自己理解。
简介
最近做项目的时候遇到一个问题:图片在手机上的显示总是和效果图有差异,UI说是我把图片放大了,看了一下代码没有啊,这是怎么回事?最后发现是把基于1080*1920分辨率下切的图片放到了mipmap-xhdpi目录下面
说明
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {
if (opts == null) {
opts = new Options();
}
if (opts.inDensity == 0 && value != null) {
// 1
final int density = value.density;
if (density == TypedValue.DENSITY_DEFAULT) {
opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
} else if (density != TypedValue.DENSITY_NONE) {
opts.inDensity = density;
}
}
if (opts.inTargetDensity == 0 && res != null) {
// 2
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
}
return decodeStream(is, pad, opts);
}
value.density
If the Value came from a resource, this holds the corresponding pixel density.
翻译一下大概是如果value来自一个resource,保存相应的像素密度(资源文件所对应的密度)
资源的density保存到inDensity中
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
屏幕的密度保存到inTargetDensity
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
// This function takes ownership of the input stream. Since the SkAndroidCodec
// will take ownership of the stream, we don't necessarily need to take ownership
// here. This is a precaution - if we were to return before creating the codec,
// we need to make sure that we delete the stream.
std::unique_ptr<SkStreamRewindable> streamDeleter(stream);
// Set default values for the options parameters.
int sampleSize = 1;
bool onlyDecodeSize = false;
SkColorType prefColorType = kN32_SkColorType;
bool isMutable = false;
float scale = 1.0f; //缩放比
bool requireUnpremultiplied = false;
jobject javaBitmap = NULL;
// Update with options supplied by the client.
if (options != NULL) {
......
javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
const int density = env->GetIntField(options, gOptions_densityFieldID);
const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
if (density != 0 && targetDensity != 0 && density != screenDensity) {
//缩放比例是屏幕的密度/资源的密度
scale = (float) targetDensity / density;
}
}
}
.......
// Scale is necessary due to density differences.
if (scale != 1.0f) {
willScale = true;
scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f);
scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
}
}
总结
inDensity代表所取的文件夹的密度,即如果手机是从xxhdpi里面取资源,那么inDensity = 320~480,如果是从hdpi文件夹里面取资源,inDensity = 240~320,依次类推
手机是根据什么规则去取那个文件夹下面的资源呢?例如我的手机分辨率是1080*1920,即手机屏幕的inTargetDensity= 480,那么inDensity = 480的drawable-xxhdpi文件夹下的图片就是最适合的图片,因此,如果drawable-xxhdpi文件夹下有这张图就会优先被使用,但是,如果drawable-xxhdpi文件夹下没有这张图时, 系统就会自动去其它文件夹下找这张图了,优先会去更高密度的文件夹下找这张图片,如果还是没找到,会尝试再找更高密度的文件夹,发现没有更高密度的了,那么就会去更低密度的文件夹下面找,依次是drawable-xhdpi -> drawable-hdpi -> drawable-mdpi -> drawable-ldpi
分辨率为1920*1080,它表示屏幕的X方向上有1080 个像素,Y方向上有1920 个像素。
dpi和densityDpi ,表示每英寸上的像素点个数,也常为密度。 5.5寸手机的含义是对角线的距离是5.5英寸,你要计算手机屏幕的密度,要先知道对角线上有多少个像素,让后再用 像素/5.5。
1080*1920分辨率的手机,inTargetDensity= 480,将一张图片放到xxhdpi文件夹下(inDensity = 480),scale = 1;所以图片的长宽不变,如果放到xhdpi文件夹下(inDensity =320),scale = 1.5,图片被放大
A,B代表两个不同宽度但是同为1080*1920分辨率的手机,可以看出A手机宽度较窄,但是他只要是在这段距离上有1080个像素,那么它依旧是1080的手机,只不过如果我们放一张96*96的图片在上面,A手机较小,B手机较大,但是这两张图片没有没缩放,都是96px*96px。内存问题。1080p分辨率的手机下,如果你把资源放到xhdpi下,内存不会减小,反而会增大1.5倍, 720p下不变,如果到xxhdpi下,1080p下不变,720p下回减小(当然你可以忍受图片变小)
密度 | dpi范围 |
---|---|
ldpi | 0dpi~120dpi |
mdpi | 120dpi~160dpi |
hdpi | 160dpi~240dpi |
xhdpi | 240dpi~320dpi |
xxhdpi | 320dpi~480dpi |
xxxhdpi | 480dpi~640dpi |
private void getDisplayInfo(){
Resources resources=getResources();
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
//当前设备的densityDpi和160的比值
float density = displayMetrics.density;
//获取设备的dpi值
int densityDpi = displayMetrics.densityDpi;
}
输出结果:
density=3.0
densityDpi=480
为什么是densityDpi和160的比值??因为dpi=160时1px=1dp,所以在dpi = 480的手机上,dp = UI标注的px / 3.0;
dpi | 比例 |
---|---|
0~120 | 1dp = 0.75px |
120~160 | 1dp = 1px |
160~240 | 1dp = 1.5px |
240~320 | 1dp = 2px |
320~480 | 1dp = 3px |
480~640 | 1dp = 4px |
图片最终大小 =( 屏幕的密度/所取的文件夹密度)* 图片大小;