前言

Android 开发中,假如咱们不确定图片的宽高,又想让 ImageView 以固定的宽度或高度显现,且图片宽高比保持不变,咱们很容易想到 adjustViewBounds 这个属性,合作固定的 ImageView 宽度或高度,即可实现宽/高固定,另一边自适应。

原因

有一个即时通讯产品,Emoji 表情经过服务端接口下发资源,支持动态增删,一起本地有一份兜底数据,用于网络不可用时的兜底展现。有一天产品在后台新增了几个表情,测验发现个别手机上新上的表情显现比较小,而本地兜底的表情则显现正常。

你真的了解 ImageView 的 adjustViewBounds 属性吗?

这儿的表情面板运用 RecyclerView 实现,Emoji Item 运用 RelativeLayout 作为容器,Emoji 图片运用 ImageView 固定高度,经过 adjustViewBounds 使宽度根据图片宽高比自适应展现。

复现

排查进程比较弯曲,这儿直接测验复现。

新建一个布局文件,加入一个 ImageView,高度固定150dp,大于图片高度,设置 adjustViewBoundstrue

你真的了解 ImageView 的 adjustViewBounds 属性吗?

嗯,没什么问题,图片被等比例拉伸,ImageView 的宽度也自适应了,契合对 adjustViewBounds 的预期。

接下来,在 ImageView 外层嵌套一层 RelativeLayout

你真的了解 ImageView 的 adjustViewBounds 属性吗?

WTF! 意想不到的工作发生了,图片没有按照预期的宽高比进行拉伸,只是展现了本来的尺度,就好像 adjustViewBounds 历来都不存在一样!

原因

先看一下 ImageView 的丈量办法,为了便利阅读,代码做了简化。

你真的了解 ImageView 的 adjustViewBounds 属性吗?

ImageView 的丈量并不杂乱,大致能够分为以下几步:

  1. 判断是否设置 adjustViewBounds,假如设置,持续往下,不然运用常规丈量办法(即运用 Drawable 宽高、最小宽高和 background 宽高的最大值)
  2. 根据丈量形式判断是否能够调整宽度或高度,假如能够,持续往下,不然运用常规丈量办法
  3. 取得 Drawable 宽高和 View 最大宽高的最小值,得到实践宽高比,判断实践宽高比和 Drawable 是否相同,假如相同,则运用当时宽高,丈量完毕,不然持续
  4. 根据 Drawable 宽高比调整实践宽高,运用调整后的宽高作为丈量结果,丈量完毕

并没有看出什么端倪,看来问题或许不在 ImageView 这儿,那就只剩 RelativeLayout 这个“嫌疑人”了。

持续查看 RelativeLayout 的丈量办法。

RelativeLayout 的丈量办法就杂乱多了,这儿摘出要害代码

你真的了解 ImageView 的 adjustViewBounds 属性吗?

能够看出,RelativeLayout 会对子 View 丈量两次,第一次丈量水平方向,确定子 View 的宽度,第2次,运用前次丈量的宽度,再次丈量,得到子 View 实践的尺度。

这儿没看出什么问题,持续看 measureChildHorizontal 办法

你真的了解 ImageView 的 adjustViewBounds 属性吗?

本相终于浮出眼前!

留意看14-18行,在确定高度丈量形式时,假如高度是 MATCH_PARENT,则运用 EXACTLY 形式,不然运用 AT_MOST 形式。

因为咱们 ImageView 是固定高度,即固定值,因而会运用 AT_MOST 形式进行丈量,而宽度也是 AT_MOST,回想一下上面 ImageView 的丈量办法,假如宽高都是 AT_MOST,则实践巨细和 Drawable 一致,即第一次丈量的宽度是 Drawable 的实践宽度。

那高度怎么不是 Drawable 的高度呢?因为 RelativeLayout 第一次丈量只是确定了 ImageView 的宽度,第2次又根据 ImageView 的实践高度进行丈量,因而便出现了上图的状况,即 adjustViewBounds 失效了。

已然 RelativeLayout 是先丈量宽度,那我把宽度固定,是不是就没问题了,没错,咱们修改代码测验一下

你真的了解 ImageView 的 adjustViewBounds 属性吗?

到这儿,咱们基本理清了 RelativeLayout 的丈量方法对 ImageViewadjustViewBounds 属性的影响,最后咱们回到事务上,为什么只要新上的表情(经过接口获取)显现偏小,而内置图片确能正常显现呢?

原因是内置图片的尺度放在对应 dpi 目录下,会根据设备 dpi 进行缩放,因而即便 adjustViewBounds 失效,也能拉伸到对应尺度,因而显现正常,而经过接口下载的图片,density 默许和设备一致,因而在 density 比较高的设备上,无法主动拉伸到预期的尺度。

处理

弄清楚了问题的原因就很容易处理了,这儿 Emoji 容器中只要一个 ImageView 保持居中,不依赖 RelativeLayout 的特性,因而直接替换为 FrameLayout 即可。

总结

本文主要经过一个事务 bug,经过源码解读,发现 ImageViewRelativeLayout 组合运用,在特定场景下 adjustViewBounds 属性会“失效”的问题,终究经过替换为 FrameLayout 来处理。

不清楚这个问题是 Google 是有意为之,还是一个 Bug,现在官方文档上暂时未发现有相关阐明,有了解的大佬能够帮小弟解解惑。