Android常用度量单位:

  1. px:是Pixel的缩写,也就是说像素
  2. inch:是指英寸,设备对角线的长度
  3. dpi:它表示每英寸上的像素点个数,也就是屏幕密度。例如手机分辨率为1920*1080,先利用勾股定理得其对角线的像素值为2202.91,再除以对角线的长度5,计算出440.582便是该设备的屏幕密度dpi。
  4. dp:android中常用的使用单位,不要与dpi混淆,下面会详细介绍dp
    ldpi、mdpi、hdpi、xhdpi、xxhdpi 这在android开发中非常常见,android根据dpi把设备分成多个级别,详细关系见下图:

    可以通过代码获取设备dpi:
 Resources resources=getResources();    DisplayMetrics displayMetrics = resources.getDisplayMetrics();    float density = displayMetrics.density;    int dpi = displayMetrics.densityDpi;

假设当前设备dpi为480,density就为3。density是一个倍数关系,是当前设备dpi除以160所得。当设备dpi为160时,1px=1dp,所以dpi为480时 1dp=3px

图片加载:

通常为了适配以及防止图片失真,会在mdpi,xhdpi,xxhdpi这类文件夹下分别放入对应的尺寸的图。那么为什么需要这样放,如果把本应该放入xxhdpi的图放入mdpi会发生什么呢
经过测试会发现 假设你的手机显示类别为xxhdpi,图片尺寸100*100.把它放入xxhdpi文件中,然后通过下面代码获取图片宽高:

            BitmapDrawable bitmapDrawable = (BitmapDrawable) imageview.getDrawable();            if (null != bitmapDrawable) {                Bitmap bitmap = bitmapDrawable.getBitmap();                int width = bitmap.getWidth();                int height = bitmap.getHeight();            }

打印宽高会发现和图片原始尺寸一下是100*100.然后把图片移到mdpi中,运行后会发现图片被放大了3倍,变成了300*300。
从图片加载源码中找找关系,首先来看BitmapFactory中的的decodeResource()方法:

public static Bitmap decodeResource(Resources res, int id, Options opts) {    Bitmap bm = null;    InputStream is = null;    try {        final TypedValue value = new TypedValue();        is = res.openRawResource(id, value);        bm = decodeResourceStream(res, value, is, null, opts);    } catch (Exception e) {    } finally {        try {            if (is != null) is.close();        } catch (IOException e) {        }    }    if (bm == null && opts != null && opts.inBitmap != null) {        throw new IllegalArgumentException("Problem decoding into existing bitmap");    }    return bm;}

typevalue保存了和当前设备dpi最接近的图片所在文件夹的destinydpi。比如图片在xxhdpi中 desinydpi就是480.接着看decodeResourceStream方法:

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) {        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) {        opts.inTargetDensity = res.getDisplayMetrics().densityDpi;    }         return decodeStream(is, pad, opts);}

value.density被赋值给opts.inDensity,保存了图片所在文件夹的dpi 如果放在xxhdpi中 那就是480
opts.inTargetDensity = res.getDisplayMetrics().densityDpi 可以看出inTargetDensity保存的当前设备的dpi。
接着往下看decodeStream方法:

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {        // we don't throw in this case, thus allowing the caller to only check        // the cache, and not force the image to be decoded.        if (is == null) {            return null;        }        Bitmap bm = null;        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");        try {            if (is instanceof AssetManager.AssetInputStream) {                final long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();                bm = nativeDecodeAsset(asset, outPadding, opts);            } else {                bm = decodeStreamInternal(is, outPadding, opts);            }            if (bm == null && opts != null && opts.inBitmap != null) {                throw new IllegalArgumentException("Problem decoding into existing bitmap");            }            setDensityFromOptions(bm, opts);        } finally {            Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);        }        return bm;    }

在该方法中会调用decodeStreamInternal();它又会继续调用nativeDecodeStream( ),该方法是native的;在BitmapFactory.cpp可见这个方法内部又调用了doDecode()它的核心源码如下:

static jobject doDecode(JNIEnv*env,SkStreamRewindable*stream,jobject padding,jobject options) {......    if (density != 0 && targetDensity != 0 && density != screenDensity) {        scale = (float) targetDensity / density;    }......if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {scaledWidth = int(scaledWidth * scale + 0.5f);scaledHeight = int(scaledHeight * scale + 0.5f);}if (willScale) {const float sx = scaledWidth / float(decodingBitmap.width());const float sy = scaledHeight / float(decodingBitmap.height());......SkPaint paint;SkCanvas canvas(*outputBitmap);canvas.scale(sx, sy);canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);}......}

可以看到缩放比scale就等于opts.inTargetDensity/opts.inDensity,也就是屏幕dpi除以图片所在文件夹dpi,所以如果把原本在xxhdpi(480)文件夹中图片误放到mdpi(160)中 opts.inTargetDensity/opts.inDensity值就会增大,所以图片被放大到3倍。
正因为会自动选择缩放图片 我们只在xxhdpi中放了一套图 使用mdpi的手机来显示图片就会自动把图片缩小。所以现在很多开发者为了缩小apk大小会选择只在最大众的文件夹中放入一套图 比如xxhdpi。
如果不想让图片被缩放 可以试试把图片放到drawable-nodpi文件夹中哦。

更多相关文章

  1. Android(安卓)LayoutInflater原理解析
  2. Android官方架构组件LiveData: 观察者模式领域二三事
  3. Android(安卓)UnitTest
  4. Android(安卓)开源框架Universal-Image-Loader完全解析(一)--- 基
  5. 某android平板项目开发笔记--自定义sharepreference UI
  6. Android(安卓)小米盒子游戏手柄按键捕获 - 能获取到的 home 键依
  7. 深入探索 Android(安卓)内存优化(炼狱级别)
  8. [Unity3D]Unity3D游戏开发之Unity与Android交互调用研究
  9. Android(安卓)程序之在线词典[2010-05-08更新图片]

随机推荐

  1. Android(安卓)Handler内存泄漏解决方法
  2. Android(安卓)TabHost布局
  3. Android之TextView
  4. XML的pull解析
  5. 切换Activity时的动画overridePendingTra
  6. [Android]应用语言切换的三种方法
  7. Android金毛狮王之Service
  8. 如何同时启动两个Android模拟器
  9. Android开发秘籍学习笔记(十)
  10. Android异步消息处理机制(源码分析+面试题