准备画布、画笔
自定义一个 TextView,在布局中使用
class SimpleColorChangeTextView : AppCompatTextView {
// ……
}
<top.xlxs.drawtext.view.SimpleColorChangeTextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
在自定义 View 中准备好画笔,onDraw 方法画需要的内容
配置画笔
一般为配置字体尺寸、反锯齿、填充方式
// 画笔配置
mTextPaint.apply {
textSize = 80f
isAntiAlias = true
style = Paint.Style.FILL
}
文字绘制辅助线
- top 文字绘制空间的最高处
- bottom 文字绘制空间的最低处
- baseline 文字绘制的基线,例如写单词时,每个字母都会参照倒数第二条线的位置
- ascent baseline 到文字顶部的平均距离,一般比 top 小一点(非数值,因为为负的)
- descent baseline 到文字底部的平均距离,一般比 bottom 小一点
FontMetrics
一个封装了 top、bottom、ascent、descent 的对象,配置好画笔字体尺寸后,就可以从画笔取到该对象,直接使用
val fontMetrics = mTextPaint.fontMetrics
水平居中绘制
x 值设为中间值,对齐方式设置为居中
mTextPaint.textAlign = Paint.Align.CENTER
canvas.drawText(mText, x, baseline + mTextPaint.fontSpacing, mTextPaint)
垂直居中绘制
- 首先新建一个图层(保存原位置,画好新的再恢复)
- 获取文字宽度,对齐方式,计算 x 轴
- 计算 baseline 位置:屏幕中间位置 + (ascent + descent)/2 - descent ,加上之后刚好多出 descent 的位置,所以最后要减去
val fontMetrics = mTextPaint.fontMetrics
// height一半 加上 descent、ascent和的一半 刚好会多出descent
val baseline = height / 2 + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent
Canvas 绘制
层、保存和恢复
跟 PS 里的图层相近,可以这样看:
每次 canvas.save() 后,后面再绘制是在一个新的图层,当再次调用后 cavas.restore() 后,又会恢复到之前的图层
文字颜色渐变绘制
覆盖的原理是画布仅在文字上方的矩形区域画。实现通过裁剪画布,仅覆盖在文字上方需要的部分。
在文字上方矩形绘制相同的文字,只是颜色不一样。通过动画动态改变上方文字绘制的区域,达到渐变的效果
覆盖绘制
private fun drawCenterText(canvas: Canvas) {
canvas.save()
val textWidth = mTextPaint.measureText(mText)
mTextPaint.textAlign = Paint.Align.LEFT
val left = width / 2 - textWidth / 2
val fontMetrics = mTextPaint.fontMetrics
// height一半 加上 descent、ascent和的一半 刚好会多出descent
val baseline = height / 2 + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent
canvas.drawText(mText, left, baseline, mTextPaint)
canvas.restore()
}
private fun drawCenterTextCover(canvas: Canvas) {
canvas.save()
mCoverTextPaint.color = Color.RED
val textWidth = mTextPaint.measureText(mText)
mTextPaint.textAlign = Paint.Align.LEFT
val left = width / 2 - textWidth / 2
val right = left + textWidth * mPercent
val fontMetrics = mTextPaint.fontMetrics
// height一半 加上 descent、ascent和的一半 刚好会多出descent
val baseline = height / 2 + (fontMetrics.descent - fontMetrics.ascent) / 2 - fontMetrics.descent
// 覆盖的原理是画布仅在文字上方的矩形区域画。实现通过裁剪画布,仅覆盖在文字上方需要的部分
val rect = Rect(textWidth.toInt(), 0, right.toInt(), height)
canvas.clipRect(rect)
canvas.drawText(mText, left, baseline, mCoverTextPaint)
canvas.restore()
}
属性动画动态改变绘制矩形
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView = findViewById(R.id.tv)
// 属性动画
Handler(Looper.getMainLooper()).post {
onStartLeft()
}
}
private fun onStartLeft() {
// 利用属性动画渐变改变 翻盖百分比
ObjectAnimator.ofFloat(textView, "mPercent", 0.0f, 1.0f).setDuration(5000).start()
}
过渡绘制补充
真彩色:没有过度绘制
蓝色:过度绘制 1 次
绿色:过度绘制 2 次
粉色:过度绘制 3 次
红色:过度绘制 4 次或更多次
完整代码实现参考