Android drawable不同分辨率下的适配

前言

文章基本抄袭郭霖大神的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);
    }
  1. value.density

    If the Value came from a resource, this holds the corresponding pixel density.

    翻译一下大概是如果value来自一个resource,保存相应的像素密度(资源文件所对应的密度)

    资源的density保存到inDensity中

  2. 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);
    }

}

总结

  1. inDensity代表所取的文件夹的密度,即如果手机是从xxhdpi里面取资源,那么inDensity = 320~480,如果是从hdpi文件夹里面取资源,inDensity = 240~320,依次类推

  2. 手机是根据什么规则去取那个文件夹下面的资源呢?例如我的手机分辨率是1080*1920,即手机屏幕的inTargetDensity= 480,那么inDensity = 480的drawable-xxhdpi文件夹下的图片就是最适合的图片,因此,如果drawable-xxhdpi文件夹下有这张图就会优先被使用,但是,如果drawable-xxhdpi文件夹下没有这张图时, 系统就会自动去其它文件夹下找这张图了,优先会去更高密度的文件夹下找这张图片,如果还是没找到,会尝试再找更高密度的文件夹,发现没有更高密度的了,那么就会去更低密度的文件夹下面找,依次是drawable-xhdpi -> drawable-hdpi -> drawable-mdpi -> drawable-ldpi

  3. 分辨率为1920*1080,它表示屏幕的X方向上有1080 像素,Y方向上有1920 像素。

  4. dpi和densityDpi ,表示每英寸上的像素点个数,也常为密度。 5.5寸手机的含义是对角线的距离是5.5英寸,你要计算手机屏幕的密度,要先知道对角线上有多少个像素,让后再用 像素/5.5。

  5. 1080*1920分辨率的手机,inTargetDensity= 480,将一张图片放到xxhdpi文件夹下(inDensity = 480),scale = 1;所以图片的长宽不变,如果放到xhdpi文件夹下(inDensity =320),scale = 1.5,图片被放大

  6. 这里写图片描述
    A,B代表两个不同宽度但是同为1080*1920分辨率的手机,可以看出A手机宽度较窄,但是他只要是在这段距离上有1080个像素,那么它依旧是1080的手机,只不过如果我们放一张96*96的图片在上面,A手机较小,B手机较大,但是这两张图片没有没缩放,都是96px*96px。

  7. 内存问题。1080p分辨率的手机下,如果你把资源放到xhdpi下,内存不会减小,反而会增大1.5倍, 720p下不变,如果到xxhdpi下,1080p下不变,720p下回减小(当然你可以忍受图片变小)

密度dpi范围
ldpi0dpi~120dpi
mdpi120dpi~160dpi
hdpi160dpi~240dpi
xhdpi240dpi~320dpi
xxhdpi320dpi~480dpi
xxxhdpi480dpi~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~1201dp = 0.75px
120~1601dp = 1px
160~2401dp = 1.5px
240~3201dp = 2px
320~4801dp = 3px
480~6401dp = 4px

图片最终大小 =( 屏幕的密度/所取的文件夹密度)* 图片大小;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值