这个项目是什么
TypeWriterTextView 是一个用 Kotlin 写的 Android 自定义 View,作用是把一段文字逐字显示出来,做出打字机的效果。和最简单的逐字显示相比,它多做了两件事:打字过程带渐变过渡,以及允许在同一段文字里对不同区间设置不同的大小和颜色。
它适合开场白、剧情文本、引导页这类需要一点节奏感的界面。README 用同一段示例文本,分别演示了普通打字、分段字号、分段颜色、字号加颜色四种调用方式。
核心实现
从 README 暴露的接口看,这个控件把"打字"拆成了几个可配置的维度:
- 节奏:
setDurationPerLetter设置每个字的间隔,setInterpolator接收一个插值器,示例里用的是DecelerateInterpolator,让整段文字的推进由快变慢,而不是匀速。 - 分段样式:
setTextWithSize和setTextWithColor都接收两个列表,分别给出若干段文字的起始下标和结束下标;落在这些区间内的字符使用指定的字号或颜色,区间之外保持默认样式。setTextWithColorAndSize把两者合并到一次调用里。 - 状态回调:
setWriteStatusListener提供onWriteStart和onWriteFinish,可以在开始和结束时做别的事,比如显示跳过按钮。 - 立即完成:
setComplete跳过动画直接显示全文,适合用户不想等的情况。
逐字绘制的内部细节,比如文字怎么切分、区间边界怎么过渡,README 没有展开,需要的话直接看源码。
使用方法
普通打字:
val textNormal = findViewById<TypeWriterTextView>(R.id.mTVTextNormal)
textNormal.setText("对,控制,我有一种特殊的精神治疗剂,给你注射了之后,你就可以安心入睡了,并且醒来后记忆不会再缺失。 但是一旦12小时之内,你没有再次注射这种药剂,那你的记忆会在短时间内全部消失。注意,即使是你不睡觉,你的记忆还是会消失")
分段字号,两个列表分别表示每段文字的起止下标:
var sizeStartList = arrayListOf(5, 11)
var sizeEndList = arrayListOf(8, 15)
val textSize = findViewById<TypeWriterTextView>(R.id.mTVTextSize)
textSize.setTextWithSize("对,控制,我有一种特殊的精神治疗剂,给你注射了之后,你就可以安心入睡了,并且醒来后记忆不会再缺失。 但是一旦12小时之内,你没有再次注射这种药剂,那你的记忆会在短时间内全部消失。注意,即使是你不睡觉,你的记忆还是会消失", 60, sizeStartList, sizeEndList)
分段颜色:
var colorStartList = arrayListOf(22, 30)
var colorEndList = arrayListOf(26, 35)
val textColor = findViewById<TypeWriterTextView>(R.id.mTVTextColor)
textColor.setTextWithColor("对,控制,我有一种特殊的精神治疗剂,给你注射了之后,你就可以安心入睡了,并且醒来后记忆不会再缺失。 但是一旦12小时之内,你没有再次注射这种药剂,那你的记忆会在短时间内全部消失。注意,即使是你不睡觉,你的记忆还是会消失", Color.RED, colorStartList, colorEndList)
字号和颜色一起设置:
var colorSizeStartList = arrayListOf(15, 30)
var colorSizeEndList = arrayListOf(20, 35)
val textColorSize = findViewById<TypeWriterTextView>(R.id.mTVTextColorSize)
textColorSize.setTextWithColorAndSize("对,控制,我有一种特殊的精神治疗剂,给你注射了之后,你就可以安心入睡了,并且醒来后记忆不会再缺失。 但是一旦12小时之内,你没有再次注射这种药剂,那你的记忆会在短时间内全部消失。注意,即使是你不睡觉,你的记忆还是会消失", Color.GREEN, 60, colorSizeStartList, colorSizeEndList)
其他设置:
textNormal.setDurationPerLetter(20) //设置每个文字的速度
textNormal.setInterpolator(DecelerateInterpolator()) //设置渐变插值器
textNormal.setComplete() //瞬间打字完成
//监听打字状态
textNormal.setWriteStatusListener(object : WriteStatusListener {
override fun onWriteStart(view: TypeWriterTextView) {
Log.e("@@", "开始打字啦")
}
override fun onWriteFinish(view: TypeWriterTextView) {
Log.e("@@", "打字完毕啦")
}
})
踩坑与思考
README 里有一句提示:效果图丢帧,请下载 demo 查看。GIF 录屏对逐字动画的还原本来就有限,帧率一低,渐变过渡的观感会失真,所以评估这类效果时最好跑一遍真机 demo。
另外,分段样式依赖调用方手算下标。文字一改,列表里的数字就要跟着改,在文案频繁变动的场景里比较麻烦。如果要在自己的项目里用,可以再包一层按关键词或标点自动计算区间的逻辑。