需求
- 一行文字时横向滚动(横向跑马灯效果)
- 多行文字时竖向滚动(竖向跑马灯效果,非翻页效果)
因为
- android自带的跑马灯需要焦点才能滚动,但实际开发中,可能会出现焦点被抢占的情况
- 第二种竖向跑马灯效果在网络上没有搜到轮子
所以 决定自己搞一个
效果
图片卡段,实际很丝滑
代码
代码不多,直接贴
原理是重写TextView中的onDraw方法,所以有一些原生TextView属性不会再生效
但是现在的效果已经满足我的需求,所以没有继续添加,后续有需要再继续添加 无非是将原生的TextView属性再重新设置下
调用该方法设置文字
setText(text: String, maxLines: Int, isScroll: Boolean)
package com.cn.markdown.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.text.TextUtils
import android.text.TextUtils.TruncateAt
import android.util.AttributeSet
import androidx.annotation.NonNull
import androidx.appcompat.widget.AppCompatTextView
/**
* @author : 鹿小柒丶
* date : 2023年11月17日14:09:43
* desc : 内容可以滚动的 TextView
*/
class ScrollingTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int = 0) : AppCompatTextView(context, attrs, defStyleAttr) {
companion object {
/**
* 移动速度
*/
private const val SPEED = 1
/**
* 横向间距
*/
private const val PADDING = 50
}
private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
/**
* 文本宽度
*/
private var textWidth = 0f
/**
* 文本高度
*/
private var textHeight = 0f
/**
* 移动偏移量
*/
private var offset = 0f
/**
* 文本内容
*/
private val textList = ArrayList<String>()
/**
* TextView的 ellipsize
*/
private var mEllipsize: TruncateAt? = null
/**
* 最大行数
*/
private var mMaxLines = -100
/**
* 是否需要滚动
*/
private var isScroll = false
/**
* 是否是竖向滚动
*/
private var isVerticalScrolling: Boolean = false
/**
* 是否是横向滚动滚动
*/
private var isHorizontalScrolling: Boolean = false
init {
textPaint.textSize = textSize
textPaint.color = currentTextColor
textHeight = getHeight(textPaint)
mEllipsize = ellipsize
}
/**
* @param text 文本内容
* @param maxLines 最大行数
* @param isScroll 是否需要滚动
*/
fun setText(text: String, maxLines: Int, isScroll: Boolean) {
this.mMaxLines = maxLines
this.isScroll = isScroll
this.offset = 0f
this.isVerticalScrolling = false
this.isHorizontalScrolling = false
this.textList.clear()
this.ellipsize = if (isScroll) {
null
} else {
mEllipsize
}
this.setText(text, BufferType.NORMAL)
this.maxLines = maxLines
}
override fun requestLayout() {
super.requestLayout()
//如果设置的最大行数 和 TextView的最大行数相等,才去执行
if (mMaxLines == maxLines && !TextUtils.isEmpty(text) && isScroll) {
if (maxLines == 1) {
//如果是一行,判断是否需要横向滚动
textWidth = textPaint.measureText(text.toString())
isHorizontalScrolling = textWidth > width
} else {
//如果是一行,判断是否需要竖向滚动,并将每一内容放到list中
//这里需要放到 post 中执行,否则获取的 lineCount 会错误
post {
isVerticalScrolling = textHeight * lineCount > height
if (isVerticalScrolling) {
for (index in 0 until lineCount) {
val start = layout.getLineStart(index)
val end = layout.getLineEnd(index)
val mText = text
.subSequence(start, end)
.toString()
textList.add(mText.replace("\n", ""))
}
textList.add("")
}
invalidate()
}
}
}
}
override fun onDraw(canvas: Canvas) {
when {
isVerticalScrolling -> { //竖向滚动
val totalTextHeight = textHeight * textList.size
textList.forEachIndexed { index, text ->
var y = (offset + textHeight * (index + 1)) % totalTextHeight
if (y <= 0) {
y += totalTextHeight
}
canvas.drawText(text, 0f, y, textPaint)
}
offset -= SPEED
postInvalidate()
}
isHorizontalScrolling -> { //横向滚动,为了实现跑马灯效果,将同一段文字绘制了两次,实现收尾相连的跑马灯效果
canvas.drawText(text.toString(), offset, baseline.toFloat(), textPaint)
canvas.drawText(text.toString(), offset + textWidth + PADDING, baseline.toFloat(), textPaint)
if (textWidth + offset <= 0) {
offset += textWidth + PADDING
}
offset -= SPEED
postInvalidate()
}
else -> { //不滚动
super.onDraw(canvas)
}
}
}
/**
* 获取字体绘制一行高度
* @return float
*/
private fun getHeight(@NonNull paint: Paint): Float {
val fm = paint.fontMetrics
return fm.descent - fm.ascent + fm.leading
}
}