You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:Android中用Kotlin实现从下到上滚动文本动画

Android文本垂直滚动(从下到上)实现方案

我已通过Android内置属性实现文本水平滚动:

android:marqueeRepeatLimit="marquee_forever"
android:padding="10dp"
android:scrollHorizontally="true"

但需要实现从下到上的垂直滚动,尝试过AI生成的自定义VerticalMarqueeTextView类、对应布局配置,以及修改原生AppCompatTextView的XML属性,均未成功实现效果,因对Kotlin了解有限,不清楚如何正确启动动画,寻求可行方案。


原自定义View的核心问题

之前的VerticalMarqueeTextView存在几个关键缺陷:

  1. init块中设置isSingleLine = true和setHorizontallyScrolling(true),限制了文本为单行水平滚动,与垂直滚动需求冲突
  2. 在init阶段调用startMarquee()时,View未完成测量,height值为0,动画位移参数无效
  3. 动画起始/结束位置设置不合理,未考虑文本实际高度与容器的适配关系

可行实现方案

方案1:改进自定义VerticalMarqueeTextView

重写自定义View,解决测量问题并调整动画逻辑:

package com.brainintelcorp.brainintel

import android.content.Context
import android.util.AttributeSet
import android.view.animation.Animation
import android.view.animation.TranslateAnimation
import androidx.appcompat.widget.AppCompatTextView

class VerticalMarqueeTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {

    private var scrollAnimation: TranslateAnimation? = null

    init {
        // 取消单行限制,允许文本换行
        isSingleLine = false
        // 关闭水平滚动
        setHorizontallyScrolling(false)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        // 测量完成后启动动画,确保获取正确高度值
        startVerticalMarquee()
    }

    private fun startVerticalMarquee() {
        // 仅当文本总高度超过View容器高度时启动滚动
        if (lineCount * lineHeight <= measuredHeight) {
            scrollAnimation?.cancel()
            return
        }

        scrollAnimation = TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0f,
            Animation.RELATIVE_TO_SELF, 0f,
            Animation.RELATIVE_TO_SELF, 0f,
            Animation.RELATIVE_TO_SELF, -1f
        ).apply {
            duration = 10000 // 滚动时长,值越小速度越快
            repeatCount = Animation.INFINITE
            repeatMode = Animation.RESTART
        }
        startAnimation(scrollAnimation)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        scrollAnimation?.cancel()
        clearAnimation()
    }
}

对应布局配置

需为View设置固定高度(或限制高度),确保文本高度超过容器时触发滚动:

<com.brainintelcorp.brainintel.VerticalMarqueeTextView
    android:id="@+id/tvQuestionContent"
    android:layout_width="match_parent"
    android:layout_height="120dp" <!-- 固定高度是触发滚动的关键 -->
    android:layout_marginTop="20dp"
    android:autoSizeMaxTextSize="100sp"
    android:autoSizeMinTextSize="14sp"
    android:autoSizeStepGranularity="1sp"
    android:autoSizeTextType="uniform"
    android:padding="10dp"
    android:text="@string/question_content"
    android:textColor="@color/primaryColor"
    android:textSize="24sp"
    android:textStyle="bold"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@id/imageLogo" />

方案2:ScrollView+Handler实现精准平滑滚动

若需要更细腻的滚动控制或自定义逻辑,可使用ScrollView嵌套TextView,通过Handler定时触发滚动:

布局文件

<ScrollView
    android:id="@+id/scrollView"
    android:layout_width="match_parent"
    android:layout_height="120dp"
    android:scrollbars="none" <!-- 隐藏默认滚动条 -->
    android:layout_marginTop="20dp"
    app:layout_constraintTop_toBottomOf="@id/imageLogo"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvQuestionContent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:autoSizeMaxTextSize="100sp"
        android:autoSizeMinTextSize="14sp"
        android:autoSizeStepGranularity="1sp"
        android:autoSizeTextType="uniform"
        android:padding="10dp"
        android:text="@string/question_content"
        android:textColor="@color/primaryColor"
        android:textSize="24sp"
        android:textStyle="bold" />
</ScrollView>

Activity逻辑实现

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.ScrollView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    private lateinit var scrollView: ScrollView
    private val handler = Handler(Looper.getMainLooper())
    private val scrollRunnable = object : Runnable {
        override fun run() {
            // 每次滚动1px,数值越大滚动速度越快
            scrollView.smoothScrollBy(0, 1)
            // 延迟10ms再次触发滚动,数值越小速度越快
            handler.postDelayed(this, 10)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        scrollView = findViewById(R.id.scrollView)

        // 延迟1秒启动滚动,可根据需求调整
        handler.postDelayed(scrollRunnable, 1000)
    }

    override fun onPause() {
        super.onPause()
        handler.removeCallbacks(scrollRunnable)
    }

    override fun onResume() {
        super.onResume()
        handler.post(scrollRunnable)
    }

    override fun onDestroy() {
        super.onDestroy()
        handler.removeCallbacks(scrollRunnable)
    }
}

内容的提问来源于stack exchange,提问作者Akash Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 07:13:16