Android加载drawable中图片后自动缩放的原理

Android加载drawable中图片后自动缩放的原理

平常开发中咱们少不了要根据设计图绘制UI,通常而言设计师给的都是设计图都是750*1334的,给的切图也通常是2x3x图。html

简单起见,咱们只将对应的2x图标放到res/drawable-xhdpi目录下便可。java

固然了,对于要求高的图标,咱们须要添加对应的多套图,分别放置到drawable-mdpidrawable-hdpidrawable-xhdpidrawable-xxhdpidrawable-xxxhdpi、目录下。android

ps: `drawable-ldpi`过低了,能够忽略掉。
复制代码

图标的容器咱们通常使用ImageView,代码以下:git

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/empty_icon" />
复制代码

实际开发中咱们直接给对应的src属性设置对应的图片资源id便可,不一样机型上的适配工做,Android系统会自动帮咱们完成。github

系统会自动根据当前机型的dpi,到对应的drawable目录下获取图片,接着再对图片进行适当的缩放操做,最后完成显示。bash

关于dpi和drawable是如何匹配的,请移步郭霖大神的Android drawable微技巧,你所不知道的drawable的那些细节app

这篇文章中有一个很重要的dpi匹配的表,以下:ide

这里我想讲下Android在获取到对应的图片后,是如何进行对应的缩放操做的。工具

实例讲解

咱们先从一个例子讲起。post

一、图片准备

咱们先准备对应的五张图片 empty_icon ,他们的分辨率分别是

mdpi		106*106
hdpi		159*159``
xhdpi		213*213
xxxhdpi 	319*319
xxxhdpi		426*426
复制代码

咱们看下官网的说明,以下图:

这说明咱们的图片是符合比例规则的,接着将他们放入到对应的drawable目录下。

ps:这几张图片来自于bravh这个库,若有侵权,请联系我,侵删。

二、展现图片信息

接着咱们将这个图片设置给ImageView对象,代码以下:

<ImageView
    android:id="@+id/iv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:src="@drawable/empty_icon" />
复制代码

而后获取当前图片的实际宽高,代码以下:

iv.post {
    var sb = StringBuffer("图片实际宽高:\n")
    sb.append("width: " + iv.width)
    sb.append("\n")
    sb.append("height: " + iv.height)

    tvIvInfo.text = sb.toString()
}
复制代码

接着运行代码,咱们看下现象,效果图以下:这个测试机的dpi为440。

咦,这里图片的尺寸怎么不对呢?drawable-xxhdpi文件夹下的图片尺寸命名是319啊,这里为什么不一致,并且咱们也没有这个尺寸的图片啊,为何呢?

莫慌,咱们此次换一个xxhdpi的模拟器试下,运行效果以下图:

此次效果正确了吧,但是这个缩放原理是什么呢?

三、图片缩放原理解析

咱们都知道Android会为咱们自动缩放drawable中的图片,但是到底缩放了多少,什么状况下缩放呢?

咱们先看下dpi和drawable的对应关系:须要说明的是,这里的范围都是不包含头包含尾,能够用(a,b]来表示。

咱们第一次运行的测试机的dpi为440,第二次的为480,他两对应的dpi等级均是xxhdpi,因此它们对应的drawable目录为drawable-xxhdpi,咱们这里对应的图片的尺寸应该是319*319

但是为啥两个手机上显示的尺寸不同呢?一个是292*292,一个是319*319。有什么规律呢?

眼尖的同窗应该已经看出来了,实际的图片尺寸缩放比例实际dpi对应dpi等级相关,规律以下:

对第一个实例来讲,实际dpi这里对应的分别是440,对应的dpi经过上面的表格能够得出为480,对应drawable图片尺寸为319px,实际显示尺寸为咱们获取到的292.

上述公式转换下,以下所示:计算实际显示尺寸的值。

针对某个图片来讲,实际dpi对应的dpi等级对应drawable下图片尺寸这三个值都是已知的,根据这三个值咱们就能够获取到实际显示的尺寸。

四、结论验证

接下来咱们验证下咱们这个结论。咱们先写一个工具类,根据实际dpi获取对应的dpi等级,代码以下所示:

/**
 * 根据实际dpi获取对应的dpi等级。
 * 登记表参见 https://blog.csdn.net/guolin_blog/article/details/50727753
 * @param srcDpi
 * @return
 */
public static int getTargetDpi(int srcDpi) {
    int targetDpi = 0;

    if (srcDpi <= 120) {// ldpi
        targetDpi = 120;
    } else if (srcDpi <= 160) {// mdpi
        targetDpi = 160;
    } else if (srcDpi <= 240) {// hdpi
        targetDpi = 240;
    } else if (srcDpi <= 320) {// xhdpi
        targetDpi = 320;
    } else if (srcDpi <= 480) {// xxhdpi
        targetDpi = 480;
    } else if (srcDpi <= 640) {// xxxhdpi
        targetDpi = 640;
    }

    return targetDpi;
}
复制代码

接着咱们再写一个方法,根据对应的dpi等级,能够获取 R.drawable.empty_icon 的实际尺寸:

/**
 * 根据对应的dpi等级,获取 R.drawable.empty_icon 对应的实际尺寸
 */
fun getDrawableSize(targetDpi: Int): Int {
    var drawableSize = 0;
    when (targetDpi) {
        120, 160 -> drawableSize = 106
        240 -> drawableSize = 159
        320 -> drawableSize = 213
        480 -> drawableSize = 319
        640 -> drawableSize = 426
    }
    return drawableSize
}
复制代码

接下来就是核心代码了,以下所示:

// 获取当前手机实际的dpi
var srcDpi = this.resources.displayMetrics.densityDpi

// 获取实际dpi对应的dpi等级
var targetDpi = ScreenUtils.getTargetDpi(srcDpi)

// 根据dpi等级,获取 R.drawable.empty_icon 实际尺寸
var drawableSize = getDrawableSize(targetDpi)

// 根据实际尺寸计算缩放后的尺寸
var resultSize = drawableSize * 1.0f / targetDpi * srcDpi

tvExpect.text = String.format("指望结果:%.2f", resultSize)
复制代码

咱们先在刚才的440dpi的手机上验证下,效果以下:

能够看出,咱们计算出的指望值四舍五入后就是实际显示的值。

接下来看下mdpi的手机上的显示效果:

接下来看下hdpi的手机上的显示效果:

接下来看下xhdpi的手机上的显示效果:

接下来看下420hdpi的手机上的显示效果:

接下来看下440hdpi的手机上的显示效果:

接下来看下xxhdpi的手机上的显示效果:

接下来看下560hdpi的手机上的显示效果:

因为Android Studio自带的模拟器中没有xxxhdpi等级的模拟器,因此这里就只能使用560dpi近似展现了,他两是一个dpi等级的。

总结

经过上述分析,咱们之后能够放心的使用Android自带的drawable文件进行图标的适配了,只要咱们正确的提供了多套图,Android系统会自动为咱们加载合适的图片,再进行适当的缩放。

以前不是很懂这个原理的时候,我都是经过给ImageView设置高度来适配的,代码以下:

<ImageView
    android:id="@+id/iv"
    android:layout_width="106dp"
    android:layout_height="106dp"
    android:layout_gravity="center_horizontal"
    android:background="@drawable/empty_icon" />
复制代码

此次搞清楚原理以后,这种比较苟的方式就能够废弃了,直接使用Android提供的默认方式便可,这样ImageView的书写方式就极其简单了:

<ImageView
    android:id="@+id/iv"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:src="@drawable/empty_icon" />
复制代码

github项目地址

具体demo地址位于: github.com/tinyvampire…

具体页面:github.com/tinyvampire…

打开方式以下:

参考

Android drawable微技巧,你所不知道的drawable的那些细节

developer.android.com/guide/topic…

相关文章
相关标签/搜索