一、 特点
基于AppCompatImageView扩展
支持圆角、圆形显示
可绘制边框,圆形时可绘制内外两层边框
支持边框不覆盖图片
可绘制遮罩
......
二、基本原理
我们要实现的图片控件继承自AppCompatImageView,它是ImageView的子类,但提供了更好的兼容性,我们在此基础上添加了若干自定义的属性和方法以实现最终的NiceImageView:
要实圆角或者圆形的显示效果,就是对图片显示的内容区域进行“裁剪”,只显示指定的区域即可。如何做呢?
一种比较直接的办法是这样的,由于图片是被绘制在画布上的,所以用canvas的clipPath()方法先将画布裁剪成指定形状,这样就能让图片按指定形状显示了,重新draw()方法即可:
这样使用src、background属性给ImageView设置显示的图片都能达到预期的显示效果。但是由于clipPath()方法不支持抗锯齿,图片边缘会有明显的毛糙感,体验并不理想,所以需要寻找其它方法。
另一种方法是使用图像的Alpha 合成模式,即
PorterDuff来实现,官方文档。这里我们使用其中的DST_IN模式。整个过程就是先绘制目标图像,也就是图片;再绘制原图像,即一个圆角矩形或者圆形,这样最终目标图像只显示和原图像重合的区域。
到这里就实现了显示为圆角或者圆形了。但是需要通过src属性或者对应的方法来设置图片,否则不能达到预期效果。
三、绘制边框
绘制边框就相对容易理解了,只需要绘制一个指定样式的圆角矩形或者圆形即可:
当图片显示为圆形时,还可以绘制一个内边框,但圆角矩形的话由于圆角大小的问题,目前只能设置一个边框咯。
但是有个问题,绘制的边框会覆盖在图片上,如果边框太宽会导致图片的可见区域变小了,影像显示效果,像这样,左下角的花盆不见了:
那么如何让边框不覆盖在图片上呢?可以在 Alpha 合成绘制前先将画布缩小一定比例,最后再绘制边框,这样问题就解决了。
缩放后的ImageView显示区域的宽高就是原宽、高分别减去2倍的边框宽度,这样缩小的比例也就显而易见了。效果如下,左下角的花盆出来了:
四、绘制遮罩
遮罩可以理解为一层带透明度的颜色,遮罩默认不绘制,当制定了遮罩颜色时才会绘制,实现很简单:
例如加一个透明度30%的红色遮罩后的效果:
核心的实现逻辑就这些了,剩下的就是自定义属性和方法了,有兴趣的可以看源码,都很简单,希望对你有所帮助吧!
更多细节及用法见GitHub:https://github.com/SheHuan/NiceImageView
五、其它
如果你需要实现类似钉钉的圆形组合头像,例如:
可以先生成对应的Bitmap,并用圆形的 NiceImageView 显示即可。如何生成组合Bitmap可以参考这里:CombineBitmap。