安卓人机验证View

效果图:

1.添加自定义属性

在values的attrs.xml 中

XML 复制代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CaptchaView">
        <attr name="captchaLength" format="integer" />
        <attr name="captchaFontSize" format="dimension" />
    </declare-styleable>
</resources>

2.自定义View

Kotlin 复制代码
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import androidx.core.content.withStyledAttributes
import kotlin.random.Random

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

    companion object {
        private const val DEFAULT_CAPTCHA_LENGTH = 4
        private const val DEFAULT_WIDTH = 160
        private const val DEFAULT_HEIGHT = 58
        private const val DEFAULT_FONT_SIZE = 24f
        private val GRAY = Color.GRAY
    }

    private var captchaCode: String = ""
    private val textPaint = Paint()
    private val linePaint = Paint()
    private val random = Random.Default

    // 自定义属性
    var captchaLength: Int = DEFAULT_CAPTCHA_LENGTH
        set(value) {
            field = value
            generateNewCaptcha()
        }
    var captchaFontSize: Float = DEFAULT_FONT_SIZE
        set(value) {
            field = value
            textPaint.textSize = value
            invalidate()
        }

    init {
        // 从XML属性读取设置
        context.withStyledAttributes(
            attrs,
            R.styleable.CaptchaView,
            defStyleAttr,
            0
        ) {

            captchaLength = getInt(
                R.styleable.CaptchaView_captchaLength,
                DEFAULT_CAPTCHA_LENGTH
            )

            captchaFontSize = getDimension(
                R.styleable.CaptchaView_captchaFontSize,
                DEFAULT_FONT_SIZE
            )

        }

        // Initialize paints
        textPaint.apply {
            textSize = captchaFontSize
            textAlign = Paint.Align.CENTER
            isAntiAlias = true
        }

        linePaint.apply {
            color = GRAY
            strokeWidth = 1f
            isAntiAlias = true
        }

        generateNewCaptcha()
    }

    fun generateNewCaptcha() {
        captchaCode = generateCaptchaCode(captchaLength)
        invalidate() // Redraw the view
    }

    fun getCaptchaCode(): String = captchaCode

    private fun generateCaptchaCode(length: Int): String {
        return (1..length).joinToString("") { Random.nextInt(10).toString() }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 获取XML中指定的宽高或使用默认值
        val width = resolveSize(DEFAULT_WIDTH, widthMeasureSpec)
        val height = resolveSize(DEFAULT_HEIGHT, heightMeasureSpec)
        setMeasuredDimension(width, height)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        // Set the background color to white
        canvas.drawColor(Color.WHITE)

        // Calculate the base vertical center of the canvas for the text
        val baseTextYPos = (height / 2 - (textPaint.descent() + textPaint.ascent()) / 2)
        val maxVerticalOffset = (captchaFontSize * 0.5f).toInt()

        // Draw each character
        val charWidth = width / captchaCode.length.toFloat()
        captchaCode.forEachIndexed { index, char ->
            val xPos = (index + 0.5f) * charWidth
            val yOffset = random.nextInt(-maxVerticalOffset, maxVerticalOffset + 1)
            val currentYPos = baseTextYPos + yOffset

            textPaint.color = Color.rgb(
                random.nextInt(256),
                random.nextInt(256),
                random.nextInt(256)
            )

            canvas.drawText(char.toString(), xPos, currentYPos, textPaint)
        }

        // Draw some random lines
        for (i in 0..3) {
            val startX = random.nextInt(width)
            val startY = random.nextInt(height)
            val endX = random.nextInt(width)
            val endY = random.nextInt(height)
            canvas.drawLine(
                startX.toFloat(),
                startY.toFloat(),
                endX.toFloat(),
                endY.toFloat(),
                linePaint
            )
        }
    }
}

3.使用

XML 复制代码
  <包名.CaptchaView
     android:id="@+id/captchaContent"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     app:captchaFontSize="28sp"
     app:captchaLength="6" />
Kotlin 复制代码
//获取显示的code
val code = binding.captchaContent.getCaptchaCode()

//刷新
binding.captchaContent.generateNewCaptcha()
相关推荐
shandianchengzi8 分钟前
【科普】安卓|安卓手机上如何简便实现Ctrl+Z(需要键盘或一台Windows电脑)
android·windows·智能手机·计算机外设·安卓·科普·记录
浮尘笔记1 小时前
Java Snowy框架CI/CD云效自动化部署流程
java·运维·服务器·阿里云·ci/cd·自动化
一点一木7 小时前
深度体验TRAE SOLO移动端7天:作为独立开发者,我把工作流揣进了兜里
前端·人工智能·trae
天外飞雨道沧桑7 小时前
TypeScript 中 omit 和 record 用法
前端·javascript·typescript
赏金术士8 小时前
Compose 教学项目
android·kotlin·compose
Lee川8 小时前
mini-cursor 揭秘:从 Tool 定义到 Agent 循环的完整实现
前端·人工智能·后端
晓梦林8 小时前
ximai靶场学习笔记
android·笔记·学习
一直不明飞行8 小时前
Java的equals(),hashCode()应该在什么时候重写
java·开发语言·jvm
REDcker8 小时前
有限状态机与状态模式详解 FSM建模Java状态模式与C++表驱动模板实践
java·c++·状态模式
canonical_entropy8 小时前
从 Spec-Driven Development 到 Attractor-Guided Engineering
前端·aigc·ai编程