自定义跑马灯控件:实现 Android 高性能滚动文本效果

1,536 阅读3分钟

在移动应用开发中,跑马灯效果(Marquee)是一种常见的用户界面交互,用于展示超出显示范围的内容。Android 自带的 TextView 提供了跑马灯效果,但有时功能有限,难以满足定制化需求。因此,我们可以通过继承 AppCompatTextView,实现一个高性能、可控速率的自定义跑马灯控件 CustomMarqueeView

功能介绍

CustomMarqueeView 具有以下特点:

  • 支持多种滚动速度(快、中、慢)。
  • 自动适配内容宽度,支持循环滚动。
  • 文本居中显示,如果内容未超出宽度,则无需滚动。
  • 可通过 XML 属性设置滚动速度。

实现代码解析

下面是 CustomMarqueeView 的完整实现及其关键部分的解析。

1. 类定义与常量设置

定义了一个继承自 AppCompatTextView 的控件,并设置了三种滚动速度常量:

companion object {
    const val SPEED_FAST = 9
    const val SPEED_MEDIUM = 6
    const val SPEED_SLOW = 3
}

开发者可以通过 setScrollSpeed 方法动态调整滚动速度。


2. 属性初始化

在构造函数中,通过自定义属性读取滚动速度:

private fun initAttrs(context: Context, attrs: AttributeSet?) {
    val typeArray = context.obtainStyledAttributes(attrs, R.styleable.CustomMarqueeView)
    mMarqueeMode = typeArray.getInt(R.styleable.CustomMarqueeView_customScrollSpeed, mMarqueeMode)
    typeArray.recycle()
}

此方法允许通过 XML 设置控件的滚动速度。

XML 示例:

<com.example.CustomMarqueeView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="这是一个跑马灯控件示例"
    app:customScrollSpeed="6" />

3. 绘制滚动效果

核心逻辑集中在 onDraw 方法。主要实现了以下功能:

  • 内容宽度检测:通过 getTextContentWidth 方法计算文本宽度。
  • 文本滚动逻辑:如果内容宽度超过控件宽度,则循环滚动;否则,文本居中显示。

滚动逻辑如下:

override fun onDraw(canvas: Canvas) {
    val textContent = text.toString().trim()
    val textWidth = getTextContentWidth()
    val charWidth = paint.measureText(" ") * 20 // 用于填充间隔
    val y = mViewHeight / 2F + getTextContentHeight() / 2

    if (textWidth > mViewWidth) {
        var x = -mScrollX % (textWidth + charWidth)
        while (x < mViewWidth) {
            canvas.drawText(textContent, x, y, paint)
            x += textWidth + charWidth
        }
        mScrollX += mMarqueeMode
        invalidate()
    } else {
        val x = (mViewWidth - textWidth) / 2F
        canvas.drawText(textContent, x, y, paint)
    }
}

核心逻辑解读:

  1. 超出宽度滚动:利用 mScrollX 控制文本的滚动位置,滚动完一轮后重置偏移。
  2. 未超出宽度居中:如果文本未超出控件宽度,则居中显示,无需滚动。

4. 动态测量与调整

onMeasure 方法确保控件的宽高正确计算,为滚动和文本绘制提供基础数据:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    mViewWidth = MeasureSpec.getSize(widthMeasureSpec)
    mViewHeight = MeasureSpec.getSize(heightMeasureSpec)
}

5. 辅助方法

以下两个方法用于获取文本的宽度和高度,保证绘制精确:

private fun getTextContentWidth(): Int {
    paint.getTextBounds(text.toString().trim(), 0, text.length, rect)
    return rect.width()
}

private fun getTextContentHeight(): Int {
    paint.getTextBounds(text.toString().trim(), 0, text.length, rect)
    return rect.height()
}

使用方法

  1. 布局文件中使用:

确保在 res/values/attrs.xml 中定义自定义属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomMarqueeView">
        <attr name="customScrollSpeed">
            <enum name="slow" value="3" />
            <enum name="medium" value="6" />
            <enum name="fast" value="9" />
        </attr>
    </declare-styleable>
</resources>

然后在布局文件中引用:

<com.example.CustomMarqueeView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="这是一个自定义跑马灯效果示例"
    app:customScrollSpeed="9" />
  1. 动态设置滚动速度:
val marqueeView = findViewById<CustomMarqueeView>(R.id.marquee_view)
marqueeView.setScrollSpeed(CustomMarqueeView.SPEED_MEDIUM)

总结

CustomMarqueeView 是一个简单易用、功能强大的自定义控件,适用于 Android 应用中需要展示滚动文本的场景。通过灵活设置滚动速度和支持文本居中显示,它可以满足大多数跑马灯效果需求,提升用户体验。

如果你在实际使用中有更多需求,还可以扩展控件,比如:

  • 支持多行文本滚动。
  • 增加滚动方向(从右向左或从左向右)。
  • 支持动态更新文本内容。