一、前言
在网上搜索相关主题,大家会发现答案千篇一律地使用9-Patch图片作为TextView的背景。这个方法我也用过,效果不好不说,还存在一些问题。
使用9-Patch文件,本质上是用图片作为背景,只是这张图片会在设置的某些像素点上重复绘制达到拉伸的效果。由于 Android 屏幕的碎片化,一张9-Patch并不能适配所有屏幕,至少需要两张分别适配 xhdpi 和 xxhdpi 。
虽然每张9-Patch不大,但是扩展到 不同场景要求不同的圆角 、 点击气泡时填充交互色变化 、 更加精细的屏幕适配 ,最终需要多款类似而有各自差别的图片集,累计成可观的安装包大小。其次用9-Patch作为消息气泡,气泡描边的视觉效果相当糟糕,这点在我的实战中得到了充分验证。
效果最好,莫过于通过代码实现消息的背景。由于通过 xml 的 Shape 样式没法绘制箭头,仅能实现描边和填充颜色,所以还得通过代码绘制的方式实现。
二、方向枚举
设置箭头的朝向,默认定义两个方向: START 为箭头朝左, END 为箭头朝右。
enum class DIRECTION { START, END }
当然还可以根据需要增加朝上和朝下的方向。虽然在Android中不建议使用枚举类,但用Android官方推荐的方法时,需要依赖的注解和Kotlin存在兼容性问题,所以这里依然使用枚举类。(Java用户请放心食用)
三、构造方法
class BubbleShape constructor(var arrowDirection: DIRECTION,
@ColorInt var solidColor: Int,
@ColorInt var strokeColor: Int,
var strokeWidth: Int,
var cornerRadius: Int,
var arrowWidth: Int,
var arrowHeight: Int,
var arrowMarginTop: Int) : Shape()
变量名已能清晰描述变量本身功能,除了这三个需额外解释:
arrowWidth 是箭头的水平宽度;
arrowHeight 箭头的垂直高度 ;
arrowMarginTop 是箭头上角距离(左、右)上方圆角的垂直高度;
通过水平宽度和垂直高度形成的矩形,就能在矩形内画出固定宽高的三角形。
四、数据成员
// 气泡上部区域的path
private val mUpperPath = Path()
// 气泡下部区域的path
private val mLowerPath = Path()
// 修正绘制stroke的偏差
private var mStrokeOffset = (strokeWidth ushr 1).toFloat()
// 修正绘制radius的偏差
private var mRadiusOffset = (cornerRadius ushr 1).toFloat()
// 预先计算以减少计算量:箭头