在文本上绘制圆角背景
假设我们需要在文本上绘制圆角背景,支持以下情况:
在一行文本上设置背景
带有圆角背景的一行文字
在两行或多行上设置文本背景
带有圆角背景的多行文字
在右到左文本中设置背景
从右到左文本与圆角背景
我们怎么能实现这个呢?请继续阅读,或直接跳转到示例代码。
跨越还是不跨越?这是个问题!
在之前的 文章中,我们已经介绍了文本的样式部分(甚至是国际化的文本)。解决方案涉及使用框架或自定义跨度。虽然在许多情况下跨度是一个很好的解决方案,但它们确实存在一些限制,使它们不适合我们的问题。外观跨度,比如BackgroundColorSpan,TextPaint允许我们访问,允许我们更改元素,如文本的背景颜色,但只能绘制纯色,不能控制角半径等元素。
使用BackgroundColorSpan的文本
我们需要与文本一起绘制一个drawable。我们可以实现自定义ReplacementSpan来自己绘制背景和文本。但是ReplacementSpans无法流入下一行,因此我们将无法支持多行背景。他们宁愿看起来像材料设计组件Chip,每个元素必须放在一条线上。
跨度工作在TextPaint级别,而不是布局级别。因此,他们无法知道文本开始和结束的行号,或者段落方向(从左到右或从右到左)
解决方案:自定义TextView
根据文本的位置,我们需要绘制四个不同的drawable作为文本背景:
文字适合一行:我们只需要一个drawable文本适合2行:我们需要文本的开头和结尾的drawable文本跨越多行:我们需要文本的开头,中间和结尾的drawable。
需要根据文本的位置绘制四个绘图
要定位背景,我们需要:
确定文本是否跨越多行找到开始和结束行根据段落方向查找开始和结束偏移所有这些都可以基于文本布局来计算。要渲染文本背后的背景,我们需要访问Canvas。自定义TextView可以访问定位可绘制和渲染它们所需的所有信息。
我们的解决方案包括将我们的问题分成4个部分并创建单独处理它们的类:
标记背景的位置是在带有Annotation跨度的XML资源中完成的,然后在代码中,我们计算位置TextRoundedBgHelper提供背景drawable作为TextView的属性 - 在中实现TextRoundedBgAttributeReader渲染drawables取决于文本是跨越一行还是多行 - TextRoundedBgRenderer接口及其实现:SingleLineRenderer和MultiLineRenderer支持自定义绘制上TextView-RoundedBgTextView,扩展一个类AppCompatTextView,读取的帮助下属性TextRoundedBgAttributeReader,将覆盖onDraw它使用TextRoundedBgHelper绘制背景。找出应该绘制背景的位置
我们通过Annotation在字符串资源中使用跨度来指定应具有背景的文本部分。从本文中了解有关使用Annotation范围的更多信息。
我们创建了一个TextRoundedBgHelper类:
使我们能够根据文本方向性定位背景:从左到右或从右到左根据drawable和水平和垂直填充渲染背景在该TextRoundedBgHelper.draw方法中,对于Annotation在文本中找到的每个跨度,我们获得跨度的开始和结束索引,找到每个的行号,然后计算开始和结束字符偏移(在行内)。然后,我们使用TextRoundedBgRenderer实现来渲染背景。
提供drawables作为属性
为了TextViews在我们的应用程序中轻松提供不同的drawable ,我们定义了4个与drawable相对应的自定义属性和2个用于水平和垂直填充的属性。我们创建了一个TextRoundedBgAttributeReader从xml布局中读取这些属性的类。