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

Android Kotlin:如何实现带红色闪烁背景的错误提示自定义EditText

Android 自定义EditText红色闪烁背景优化方案

一、解决闪烁无法按时停止的问题

你的原实现中,多次调用showErrorBlink()会创建多个独立的Handler和Runnable,导致之前的闪烁任务无法被正确停止;另外局部变量的Runnable也可能因为引用问题导致移除失效。可以通过以下方式修复:

优化后的代码实现

class BlinkingEditText(context: Context, attrs: AttributeSet?) : AppCompatEditText(context, attrs) {
    private val handler = Handler(Looper.getMainLooper())
    private var blinkRunnable: Runnable? = null
    private var isRed = false

    fun showErrorBlink() {
        // 启动新闪烁前先停止已有任务
        stopBlink()

        isRed = false
        blinkRunnable = object : Runnable {
            override fun run() {
                setBackgroundColor(if (isRed) Color.WHITE else Color.RED)
                isRed = !isRed
                blinkRunnable?.let { handler.postDelayed(it, 500) }
            }
        }

        blinkRunnable?.let { handler.post(it) }
        // 3秒后自动停止
        handler.postDelayed({ stopBlink() }, 3000)
    }

    private fun stopBlink() {
        blinkRunnable?.let { handler.removeCallbacks(it) }
        blinkRunnable = null
        // 停止后恢复白色背景
        setBackgroundColor(Color.WHITE)
    }

    // View销毁时清理任务,避免内存泄漏
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        stopBlink()
    }
}

核心改进点

  • 用成员变量保存Runnable实例,确保能精准移除任务
  • 每次启动闪烁前强制停止已有任务,避免多任务冲突
  • 在onDetachedFromWindow中清理资源,防止内存泄漏

二、更高效的实现方案(推荐)

使用Android原生动画框架或XML Drawable,比手动切换背景色更高效、易维护。

方案1:使用ValueAnimator(推荐)

利用Android动画框架的ValueAnimator控制颜色切换,系统会自动优化重绘,控制更精确:

class BlinkingEditText(context: Context, attrs: AttributeSet?) : AppCompatEditText(context, attrs) {
    private var colorAnimator: ValueAnimator? = null

    fun showErrorBlink() {
        stopBlink()

        // 创建颜色切换动画,循环实现闪烁效果
        colorAnimator = ValueAnimator.ofArgb(Color.WHITE, Color.RED, Color.WHITE).apply {
            duration = 500 // 单次颜色切换时长
            repeatCount = 5 // 重复5次,总时长3秒(500ms*6次)
            repeatMode = ValueAnimator.REVERSE
            addUpdateListener { animator ->
                setBackgroundColor(animator.animatedValue as Int)
            }
            // 动画结束/取消时恢复背景
            addListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator) {}
                override fun onAnimationEnd(animation: Animator) {
                    setBackgroundColor(Color.WHITE)
                    colorAnimator = null
                }
                override fun onAnimationCancel(animation: Animator) {
                    onAnimationEnd(animation)
                }
                override fun onAnimationRepeat(animation: Animator) {}
            })
            start()
        }
    }

    private fun stopBlink() {
        colorAnimator?.let {
            it.removeAllListeners()
            it.cancel()
        }
        colorAnimator = null
        setBackgroundColor(Color.WHITE)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        stopBlink()
    }
}

方案2:使用XML AnimationDrawable

将动画逻辑分离到XML文件中,代码更简洁:

  1. 创建res/drawable/blink_error_background.xml:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
    <item android:drawable="@color/white" android:duration="500"/>
    <item android:drawable="@color/red" android:duration="500"/>
</animation-list>
  1. 代码实现:
class BlinkingEditText(context: Context, attrs: AttributeSet?) : AppCompatEditText(context, attrs) {
    private var blinkDrawable: AnimationDrawable? = null

    fun showErrorBlink() {
        stopBlink()

        // 设置闪烁背景并启动动画
        val drawable = resources.getDrawable(R.drawable.blink_error_background, context.theme) as AnimationDrawable
        background = drawable
        blinkDrawable = drawable
        drawable.start()

        // 3秒后停止动画
        Handler(Looper.getMainLooper()).postDelayed({ stopBlink() }, 3000)
    }

    private fun stopBlink() {
        blinkDrawable?.let {
            if (it.isRunning) it.stop()
        }
        blinkDrawable = null
        // 恢复默认白色背景
        setBackgroundColor(Color.WHITE)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        stopBlink()
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 01:24:52