1 Star 79 Fork 34

John-逍遥/android_plugin_readme

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
README_verification_code.md 9.75 KB
一键复制 编辑 原始数据 按行查看 历史
cfwp007 提交于 5年前 . 完善验证码输入框

验证码输入框 代码展示

以下只展示关键部分

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >
    
    ...
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请输入验证码"
        android:textSize="20sp"
        android:layout_marginTop="20dp"
        android:layout_gravity="center_horizontal"
        />
    
    <com.lujianfei.plugin2_7.CodeEditText
        android:id="@+id/edit_verification_code"
        android:layout_marginTop="50dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        />
</LinearLayout>
MainActivity.kt
package com.lujianfei.plugin2_7

import android.content.Intent
import android.net.Uri
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import com.lujianfei.module_plugin_base.base.BasePluginActivity
import com.lujianfei.module_plugin_base.beans.PluginActivityBean


class MainActivity : BasePluginActivity() {
    
    ...
    
    private var edit_verification_code: CodeEditText? = null
    
    override fun resouceId(): Int = R.layout.activity_main

    override fun initView() {
        edit_verification_code = findViewById(R.id.edit_verification_code)       
    }
   
    override fun initEvent() {               
        edit_verification_code?.requestFocus()
        edit_verification_code?.showSoftInput()
        
        edit_verification_code?.onInputFinishListener = { code->
            Toast.makeText(that, code, Toast.LENGTH_SHORT).show()
        }
    }
	...
}

核心组件类

CodeEditText.kt
package com.lujianfei.plugin2_7

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.text.InputFilter
import android.text.InputFilter.LengthFilter
import android.util.AttributeSet
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.widget.AppCompatEditText
import com.lujianfei.module_plugin_base.utils.DensityUtils


/**
 *@date     创建时间:2020/10/20
 *@name     作者:陆键霏
 *@describe 描述:
 */
class CodeEditText @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : AppCompatEditText(context, attrs, defStyleAttr) {

    //  验证码文本颜色
    private var mTextColor  = 0
    // 输入的最大长度
    private val mMaxLength = 4
    // 边框宽度
    private val mStrokeWidth = DensityUtils.dip2px(60f)
    // 边框高度
    private val mStrokeHeight = DensityUtils.dip2px(60f)
    // 边框之间的距离
    private val mStrokePadding = DensityUtils.dip2px(20f)
    // 用矩形来保存方框的位置、大小信息
    private val mRect = Rect()
    /**
     * 输入结束监听
     */
    var onInputFinishListener: ((String)->Unit)? = null
    // 方框的背景
    private var mStrokeDrawable: Drawable? = null
    
    init {
        // 只允许数字
        inputType = EditorInfo.TYPE_CLASS_NUMBER 
        // 创建 enable 和 focused 状态下的 drawable
        val normalDrawable = DrawableHelper.createDrawable(
                fillColor = 0xffffffff.toInt(),
                roundRadius = DensityUtils.dip2px(5f).toFloat(),
                strokeWidth = DensityUtils.dip2px(1f),
                strokeColorIn = 0xffdddddd.toInt()
        )
        
        val focusedDrawable = DrawableHelper.createDrawable(
                fillColor = 0xffffffff.toInt(),
                roundRadius = DensityUtils.dip2px(5f).toFloat(),
                strokeWidth = DensityUtils.dip2px(1f),
                strokeColorIn = 0xfff89b09.toInt()
        )
        // 将 drawable 合成为 selector, 使其同时具备 enable 和 focused 不同状态的样式
        val selector = DrawableHelper.createSelector(enableDrawable = normalDrawable, focusedDrawable = focusedDrawable, normalDrawable = normalDrawable)
        
        mStrokeDrawable = selector
        // 最大字数
        setMaxLength(mMaxLength)
        // 去掉背景颜色
        setBackgroundColor(Color.TRANSPARENT)
        // 不显示光标
        isCursorVisible = false
        isFocusableInTouchMode = true
        isFocusable = true
    }

    /**
     * 设置最大长度
     */
    private fun setMaxLength(maxLength: Int) {
        filters = if (maxLength >= 0) {
            arrayOf<InputFilter>(LengthFilter(maxLength))
        } else {
            arrayOfNulls(0)
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        // 当前输入框的宽高信息
        var width = measuredWidth
        var height = measuredHeight
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        // 判断高度是否小于推荐高度
        if (height < mStrokeHeight) {
            height = mStrokeHeight
        }
        // 输入框宽度 = 边框宽度 * 数量 + 边框间距 *(数量-1)
        val recommendWidth = mStrokeWidth * mMaxLength + mStrokePadding * (mMaxLength - 1)
        // 判断宽度是否小于推荐宽度
        if (width < recommendWidth) {
            width = recommendWidth
        }
        val widthMeasureSpecAfter = MeasureSpec.makeMeasureSpec(width, widthMode)
        val heightMeasureSpecAfter = MeasureSpec.makeMeasureSpec(height, heightMode)
        // 设置测量布局
        setMeasuredDimension(widthMeasureSpecAfter, heightMeasureSpecAfter)
    }

    override fun onDraw(canvas: Canvas?) {
        mTextColor = currentTextColor
        // 在画支持设置文本颜色,把系统化的文本透明掉,相当于覆盖
        setTextColor(Color.TRANSPARENT)
        //  系统画的方法
        super.onDraw(canvas)
        // 重新设置文本颜色
        setTextColor(mTextColor)
        // 重绘背景颜色
        drawStrokeBackground(canvas)
        // 重绘文本
        drawText(canvas)
    }

    override fun onTextContextMenuItem(id: Int): Boolean {
        return false
    }

    /**
     * 绘制方框
     */
    private fun drawStrokeBackground(canvas: Canvas?) {
        if (canvas == null) return
        // 下面绘制方框背景颜色
        // 确定反馈位置
        mRect.left = 0
        mRect.top = 0
        mRect.right = mStrokeWidth
        mRect.bottom = mStrokeHeight
        val count = canvas.saveCount //  当前画布保存的状态
        canvas.save() // 保存画布
        for (i in 0 until mMaxLength) {
            mStrokeDrawable?.apply {
                bounds = mRect // 设置位置
                state = intArrayOf(android.R.attr.state_enabled) // 设置图像状态
                draw(canvas) //  画到画布上    
            }
            //  确定下一个方框的位置
            val dx = mRect.right + mStrokePadding.toFloat() // X坐标位置
            // 保存画布
            canvas.save()
            // [注意细节] 移动画布到下一个位置
            canvas.translate(dx, 0f)
        }
        // [注意细节] 把画布还原到画反馈之前的状态,这样就还原到最初位置了
        canvas.restoreToCount(count)
        // 画布归位
        canvas.translate(0f, 0f)

        // 下面绘制高亮状态的边框
        // 当前高亮的索引
        val activatedIndex = 0.coerceAtLeast(editableText.length)
        mRect.left = mStrokeWidth * activatedIndex + mStrokePadding * activatedIndex
        mRect.right = mRect.left + mStrokeWidth
        mStrokeDrawable?.apply {
            state = intArrayOf(android.R.attr.state_focused)
            bounds = mRect
            draw(canvas)       
        }
    }

    /**
     * 重绘文本
     */
    private fun drawText(canvas: Canvas?) {
        if (canvas == null) return
        val count = canvas.saveCount
        canvas.translate(0f, 0f)
        val length = editableText.length
        for (i in 0 until length) {
            val text = editableText[i].toString()
            val textPaint = paint
            textPaint.color = mTextColor
            // 获取文本大小
            textPaint.getTextBounds(text, 0, 1, mRect)
            // 计算(x,y) 坐标
            val x = mStrokeWidth / 2 + (mStrokeWidth + mStrokePadding) * i - mRect.centerX()
            val y = canvas.height / 2 + mRect.height() / 2
            canvas.drawText(text, x.toFloat(), y.toFloat(), textPaint)
        }
        canvas.restoreToCount(count)
    }

    override fun onTextChanged(
        text: CharSequence?,
        start: Int,
        lengthBefore: Int,
        lengthAfter: Int
    ) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter)
        // 当前文本长度
        val textLength = editableText.length
        if (textLength == mMaxLength) {
            hideSoftInput()
            onInputFinishListener?.invoke(editableText.toString())
        }
    }

    private fun hideSoftInput() {
        val imm =
            context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(
            windowToken,
            InputMethodManager.HIDE_NOT_ALWAYS
        )
    }

    fun showSoftInput() {
        val inputManager= context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        inputManager.showSoftInput(this, 0)
    }
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Android
1
https://gitee.com/lujianfei/android_plugin_readme.git
git@gitee.com:lujianfei/android_plugin_readme.git
lujianfei
android_plugin_readme
android_plugin_readme
master

搜索帮助