当前位置: 首页 > ai >正文

Android 短信验证码输入框实现

一、设计思路

  1. 使用6个独立输入框组成验证码区域
  2. 每个输入框只能输入一个数字
  3. 输入后自动跳转到下一个输入框
  4. 支持退格键删除并返回上一个输入框
  5. 支持一次性粘贴6位验证码

二、代码实现

2.1 xml布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="20dp"android:gravity="center"android:background="#FFF"><!-- 标题 --><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="短信验证码"android:textSize="20sp"android:textColor="#333"android:layout_marginBottom="30dp"android:textStyle="bold"/><!-- 提示信息 --><TextViewandroid:id="@+id/tvTips"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="验证码已发送至 138****8888"android:textSize="14sp"android:textColor="#666"android:layout_marginBottom="20dp"/><!-- 验证码输入区域 --><LinearLayoutandroid:id="@+id/llCodeContainer"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="30dp"android:orientation="horizontal"><!-- 6个输入框将通过代码动态添加 --></LinearLayout><!-- 重新发送按钮 --><TextViewandroid:id="@+id/tvResend"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="60秒后重新发送"android:textSize="14sp"android:textColor="#FFD700"android:layout_marginBottom="20dp"/><!-- 确定按钮 --><Buttonandroid:id="@+id/btnConfirm"android:layout_width="200dp"android:layout_height="45dp"android:text="确定"android:textColor="#FFF"android:background="@drawable/btn_confirm_bg"android:textSize="16sp"android:enabled="false"/><!-- 隐藏的输入框用于接收输入 3dp只是为了占位获取焦点,否则可能弹不出键盘 --><EditTextandroid:id="@+id/etHidden"android:layout_width="3dp"android:layout_height="3dp"android:inputType="number"android:maxLength="6"android:cursorVisible="false"android:background="@null"/></LinearLayout>
2.2 Activity 代码
package com.vc.psclient.Activityimport android.graphics.Color
import android.os.Bundle
import android.os.CountDownTimer
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.Gravity
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.vc.psclient.R
import com.vc.psclient.databinding.ActivityCodeInputBindingclass CodeInputActivity : AppCompatActivity(){private lateinit var binding: ActivityCodeInputBindingprivate lateinit var llCodeContainer: LinearLayoutprivate val codeViews = arrayOfNulls<TextView>(6)private lateinit var etHidden: EditTextprivate lateinit var btnConfirm: Buttonprivate lateinit var tvResend: TextViewprivate var countdown = 60private var countDownTimer: CountDownTimer? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_code_input)initViews()setupCodeInput()startCountdown()}private fun initViews() {llCodeContainer = findViewById<LinearLayout>(R.id.llCodeContainer)etHidden = findViewById<EditText>(R.id.etHidden)btnConfirm = findViewById<Button>(R.id.btnConfirm)tvResend = findViewById<TextView>(R.id.tvResend)// 动态创建6个输入框for (i in 0 until codeViews.size) {val textView = TextView(this)val params = LinearLayout.LayoutParams(dpToPx(40), dpToPx(40))params.setMargins(dpToPx(8), 0, dpToPx(8), 0)textView.layoutParams = paramstextView.setBackgroundResource(R.drawable.code_box_bg)textView.gravity = Gravity.CENTERtextView.textSize = 20ftextView.setTextColor(Color.BLACK)textView.isFocusable = falsellCodeContainer!!.addView(textView)codeViews[i] = textView}btnConfirm.setOnClickListener(View.OnClickListener { v: View? ->// 获取验证码val code = StringBuilder()for (codeView in codeViews) {code.append(codeView?.text.toString())}// 验证验证码if (code.length == 6) {verifyCode(code.toString())}})tvResend.setOnClickListener(View.OnClickListener { v: View? ->if (tvResend.text.toString() == "重新发送") {resendCode()startCountdown()}})}private fun setupCodeInput() {etHidden!!.addTextChangedListener(object : TextWatcher {override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}override fun afterTextChanged(s: Editable) {var input = s.toString()if (input.length > 6) {input = input.substring(0, 6)etHidden.setText(input)etHidden.setSelection(6)return}// 更新显示for (i in 0 until codeViews.size) {if (i < input.length) {codeViews[i]!!.text = input[i].toString()} else {codeViews[i]!!.text = ""}}// 检查是否全部填满val allFilled = input.length == 6btnConfirm!!.isEnabled = allFilled// 更新UI状态updateBoxesStyle(input.length)}})// 设置隐藏输入框的点击监听,确保点击任何输入框区域都能激活输入llCodeContainer!!.setOnClickListener { v: View? ->Log.d("BBBBB","激活输入")etHidden.requestFocus()val imm =getSystemService(INPUT_METHOD_SERVICE) as InputMethodManagerimm.showSoftInput(etHidden, InputMethodManager.SHOW_IMPLICIT)
//            EditTextUtils.showSoftInputFromWindow(etHidden)}// 处理删除键etHidden.setOnKeyListener { v: View?, keyCode: Int, event: KeyEvent ->if (keyCode == KeyEvent.KEYCODE_DEL && event.action == KeyEvent.ACTION_DOWN) {val currentText = etHidden.text.toString()if (currentText.length > 0) {etHidden.setText(currentText.substring(0, currentText.length - 1))etHidden.setSelection(etHidden.text.length)return@setOnKeyListener true}}false}}private fun updateBoxesStyle(length: Int) {for (i in 0 until codeViews.size) {if (i == length) {// 当前焦点位置codeViews[i]!!.setBackgroundResource(R.drawable.code_box_bg_focused)} else {codeViews[i]!!.setBackgroundResource(R.drawable.code_box_bg)}}}private fun startCountdown() {countDownTimer = object : CountDownTimer(60000, 1000) {override fun onTick(millisUntilFinished: Long) {countdown = (millisUntilFinished / 1000).toInt()tvResend!!.text = countdown.toString() + "秒后重新发送"tvResend!!.setTextColor(Color.GRAY)tvResend!!.isClickable = false}override fun onFinish() {countdown = 0tvResend!!.text = "重新发送"tvResend?.setTextColor(-0x2900)tvResend?.isClickable = true}}.start()}private fun resendCode() {// 实现重新发送验证码的逻辑Toast.makeText(this, "验证码已重新发送", Toast.LENGTH_SHORT).show()etHidden!!.setText("")}private fun verifyCode(code: String) {// 实现验证码验证逻辑Toast.makeText(this, "正在验证: $code", Toast.LENGTH_SHORT).show()// 这里通常是网络请求验证}private fun dpToPx(dp: Int): Int {return (dp * resources.displayMetrics.density).toInt()}override fun onDestroy() {super.onDestroy()countDownTimer?.cancel()}}
2.3 xml选中效果布局
!-- res/drawable/code_box_bg.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"><solid android:color="#FFF" /><stroke android:width="1dp" android:color="#DDD" /><corners android:radius="4dp" />
</shape><!-- res/drawable/code_box_bg_focused.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"><solid android:color="#FFF" /><stroke android:width="2dp" android:color="#FFD700" /><corners android:radius="4dp" />
</shape><!-- res/drawable/btn_confirm_bg.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android"><item android:state_enabled="true"><shape><solid android:color="#FFD700" /><corners android:radius="4dp" /></shape></item><item android:state_enabled="false"><shape><solid android:color="#DDD" /><corners android:radius="4dp" /></shape></item>
</selector>

三、效果图

http://www.xdnf.cn/news/19085.html

相关文章:

  • 嵌入式Linux驱动开发:定时器驱动
  • 北斗传输采集数据的自定义通信协议
  • 香港电讯创新解决方案,开启企业数字化转型新篇章
  • CollageIt:简单易用的照片拼贴工具
  • Spring boot 启用第二数据源
  • 【Day 40】Shell脚本-条件判断
  • linux中.tar 解压命令
  • 【系列05】端侧AI:构建与部署高效的本地化AI模型 第4章:模型量化(Quantization)
  • 嵌入式Linux驱动开发 - DTS LED驱动
  • 管家婆辉煌ERP中如何查询畅销商品
  • java8浮点型算平均值
  • 37 HTB Remote 机器 - 容易
  • 字典解密助手ArchiveHelperWpfv1.0.12详细使用说明书
  • Apisix工作流程
  • 界面钝化新策略:华南理工实现泡沫铜/Bi-In相变材料热循环性能显著增强
  • 直流电机驱动与TB6612
  • Excel数组学习笔记
  • 【开题答辩全过程】以 基于JSP的养生网站系统为例,包含答辩的问题和答案
  • 本地部署商业服务器 Glassfish 并实现外部访问
  • Rust 安装与运行指南
  • Jetson进行旋转目标检测推理实现大疆无人机飞行控制
  • Git 9 ,.git/index.lock 文件冲突问题( .git/index.lock‘: File exists. )
  • 卷积神经网络为什么要填充(Padding)
  • 基于无人机的风电叶片全自动智能巡检:高精度停角估计与细节优先曝光调控技术
  • 在做题中学习(89):合并区间
  • 如何去除edge浏览器的灰色边框
  • idea2023.3遇到了Lombok失效问题,注释optional和annotationProcessorPaths即可恢复正常
  • Redis与MySQL数据不一致问题
  • 【MYSQL | 基础篇 多表查询】
  • FunctionAI 图像生成:简化从灵感到 API 调用的每一步