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

如何为TextView创建自定义视图并实现点击动态切换状态及动画恢复

实现方案说明

你确实需要添加init{}代码块来完成视图初始化、初始状态配置、点击事件绑定等基础逻辑,完整实现步骤和代码如下:


实现步骤

  1. 先声明内部视图引用、状态标记位、两种状态对应的资源常量,避免重复点击冲突
  2. 在init块中完成TextView初始化、初始状态设置、点击事件绑定
  3. 完善onClickToCopy逻辑:点击时切换到激活状态,协程等待5秒后通过淡入淡出动画恢复初始状态

完整实现代码

class MyCustomTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    val coroutineScope: CoroutineScope,
) : ConstraintLayout(
    context,
    attrs,
    defStyleAttr
) {
    // 内部承载文字和drawable的TextView
    private val contentTv: TextView
    // 状态标记:避免重复触发点击逻辑
    private var isInActivatedState = false

    // 状态资源常量,可替换为你项目的实际资源,也可以通过自定义属性从xml配置
    private val normalTextColor = ContextCompat.getColor(context, R.color.green)
    private val activatedTextColor = ContextCompat.getColor(context, R.color.gray) // 替换为点击后文字颜色
    private val normalDrawable = ContextCompat.getDrawable(context, R.drawable.ic_normal)!!
    private val activatedDrawable = ContextCompat.getDrawable(context, R.drawable.ic_activated)!!
    private val normalTint = ContextCompat.getColor(context, R.color.green)
    private val activatedTint = ContextCompat.getColor(context, R.color.gray) // 替换为点击后drawable着色

    init {
        // 初始化TextView,也可以加载自定义xml布局替代手动创建
        contentTv = TextView(context).apply {
            layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
            // 初始状态配置
            setCompoundDrawablesRelativeWithIntrinsicBounds(normalDrawable, null, null, null)
            compoundDrawableTintList = ColorStateList.valueOf(normalTint)
            setTextColor(normalTextColor)
            compoundDrawablePadding = 8.dp2px(context) // 文字和drawable的间距,按需调整
            text = "点击复制" // 按需修改展示文字
        }
        addView(contentTv)
        // 绑定点击事件
        setOnClickListener { onClickToCopy() }
    }

    fun onClickToCopy(){
        if (isInActivatedState) return
        isInActivatedState = true
        // 切换到激活状态
        updateViewState(activatedTextColor, activatedDrawable, activatedTint)

        coroutineScope.launch {
            delay(5000L)
            // 切回主线程执行恢复动画
            withContext(Dispatchers.Main) {
                // 300ms淡入淡出动画,时长可调整
                val fadeOut = AlphaAnimation(1f, 0f).apply { duration = 150 }
                val fadeIn = AlphaAnimation(0f, 1f).apply { duration = 150; startOffset = 150 }
                val animSet = AnimationSet(true).apply {
                    addAnimation(fadeOut)
                    addAnimation(fadeIn)
                }
                // 淡出结束后更新状态,再淡入展示初始状态
                fadeOut.setAnimationListener(object : Animation.AnimationListener {
                    override fun onAnimationStart(animation: Animation?) {}
                    override fun onAnimationRepeat(animation: Animation?) {}
                    override fun onAnimationEnd(animation: Animation?) {
                        updateViewState(normalTextColor, normalDrawable, normalTint)
                    }
                })
                startAnimation(animSet)
                isInActivatedState = false
            }
        }
    }

    // 统一更新视图状态的工具方法
    private fun updateViewState(textColor: Int, startDrawable: Drawable, tintColor: Int) {
        contentTv.apply {
            setTextColor(textColor)
            setCompoundDrawablesRelativeWithIntrinsicBounds(startDrawable, null, null, null)
            compoundDrawableTintList = ColorStateList.valueOf(tintColor)
        }
    }

    // dp转px扩展方法,按需保留
    private fun Int.dp2px(context: Context): Int {
        return (this * context.resources.displayMetrics.density).toInt()
    }
}

注意事项

  • 如果你已经有对应xml布局,init块可以直接加载布局并通过findViewById获取TextView,不需要手动创建
  • 传入的coroutineScope建议和视图生命周期绑定,避免视图销毁后协程仍执行导致内存泄漏,推荐使用视图对应的LifecycleCoroutineScope

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 16:15:02