安卓人机验证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()
相关推荐
KobeSacre2 分钟前
ReentrantLock源码
java
嵌入式协会20240727 分钟前
(已解决)MinIO python 获取预签名出现forbidden、errornetwork等错误
java·开发语言·python
不才不才不不才30 分钟前
Spring AI 实战:聊天、提示词、记忆三件套
java·人工智能·spring·ai
#麻辣小龙虾#1 小时前
基于vue3.0开发一款【固废与废气运维管理系统】(支持源码)
前端·vue.js·vue3
Cosolar1 小时前
Docsify零构建文档站完全指南:从快速搭建到企业级部署
前端·开源·github
-SOLO-1 小时前
备份apk 工具
android
weixin_471383031 小时前
Taro-02-页面路由
前端·taro
星栈独行1 小时前
Makepad 应用如何读文件、调接口、保存数据
前端·程序人生·ui·rust·github
一 乐2 小时前
家政服务管理系统|基于springboot + vue家政服务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·家政服务管理系统
IT_陈寒2 小时前
Vite热更新失效?可能你在用Windows
前端·人工智能·后端