效果图:

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()