这里提一下,这个当时也是在网上看到一个博主写的代码改了下用在我么项目中的验证码输入框。博主的地址不记得了这里只能顺带标注一下。。。
效果图如下:
![](https://i-blog.csdnimg.cn/blog_migrate/1a156c386678de3f1b61cae532a6b323.png)
直入主题,代码如下:
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="wrap_content"
android:orientation="vertical"
android:gravity="end"
>
<TextView
android:id="@+id/tv_view_top_tip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:textColor="@color/img_code_text_error_color"
android:textSize="12sp"
android:text="error"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/ll_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:baselineAligned="false">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginRight="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
>
<TextView
android:id="@+id/tv_code1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/global_text_color_10"
android:textSize="24sp"
android:textStyle="bold"
android:background="@null"
android:layout_centerInParent="true"
android:gravity="center"/>
<View
android:id="@+id/v1_center_line"
android:layout_width="1.5dp"
android:layout_height="16dp"
android:visibility="invisible"
android:layout_centerInParent="true"
android:background="@color/mainColor"
/>
</RelativeLayout>
<View
android:id="@+id/v1"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/mainColor" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
>
<TextView
android:id="@+id/tv_code2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/global_text_color_10"
android:textSize="24sp"
android:textStyle="bold"
android:background="@null"
android:layout_centerInParent="true"
android:gravity="center"/>
<View
android:id="@+id/v2_center_line"
android:layout_width="1.5dp"
android:layout_height="16dp"
android:visibility="invisible"
android:layout_centerInParent="true"
android:background="@color/mainColor" />
</RelativeLayout>
<View
android:id="@+id/v2"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/global_text_color_grey" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
>
<TextView
android:id="@+id/tv_code3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/global_text_color_10"
android:textSize="24sp"
android:textStyle="bold"
android:background="@null"
android:layout_centerInParent="true"
android:gravity="center"/>
<View
android:id="@+id/v3_center_line"
android:layout_width="1.5dp"
android:layout_height="16dp"
android:visibility="invisible"
android:layout_centerInParent="true"
android:background="@color/mainColor"/>
</RelativeLayout>
<View
android:id="@+id/v3"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/global_text_color_grey" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
>
<TextView
android:id="@+id/tv_code4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/global_text_color_10"
android:background="@null"
android:textStyle="bold"
android:textSize="24sp"
android:layout_centerInParent="true"
android:gravity="center"/>
<View
android:id="@+id/v4_center_line"
android:layout_width="1.5dp"
android:layout_height="16dp"
android:visibility="invisible"
android:layout_centerInParent="true"
android:background="@color/mainColor" />
</RelativeLayout>
<View
android:id="@+id/v4"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/global_text_color_grey" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll5_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
>
<TextView
android:id="@+id/tv_code5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/global_text_color_10"
android:background="@null"
android:textStyle="bold"
android:textSize="24sp"
android:layout_centerInParent="true"
android:gravity="center"/>
<View
android:id="@+id/v5_center_line"
android:layout_width="1.5dp"
android:layout_height="16dp"
android:visibility="invisible"
android:layout_centerInParent="true"
android:background="@color/mainColor" />
</RelativeLayout>
<View
android:id="@+id/v5"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/global_text_color_grey" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll6_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:layout_marginLeft="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
>
<TextView
android:id="@+id/tv_code6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/global_text_color_10"
android:background="@null"
android:textStyle="bold"
android:textSize="24sp"
android:layout_centerInParent="true"
android:gravity="center"/>
<View
android:id="@+id/v6_center_line"
android:layout_width="1.5dp"
android:layout_height="16dp"
android:visibility="invisible"
android:layout_centerInParent="true"
android:background="@color/mainColor" />
</RelativeLayout>
<View
android:id="@+id/v6"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/global_text_color_grey" />
</LinearLayout>
</LinearLayout>
<EditText
android:id="@+id/et_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/ll_code"
android:layout_alignBottom="@+id/ll_code"
android:background="@android:color/transparent"
android:textColor="@android:color/transparent"
android:cursorVisible="false"
android:inputType="number"/>
</RelativeLayout>
<TextView
android:id="@+id/tv_get_sms_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="9.5dp"
android:paddingBottom="10dp"
android:textColor="@color/text_color_pressed_selector"
android:textSize="12sp"
android:text="@string/resend_verify_code"
/>
</LinearLayout>
class CustomSmsCodeInputLayout : RelativeLayout, View.OnClickListener {
/**
* 枚举中有两种状态,FOUR四个输入框,SIX6个输入框
*/
enum class InputLineNum(var num: Int) {
FOUR(4), SIX(6)
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.tv_get_sms_code -> {
clearAllInputValues()
if (!this::onUpdateListener.isInitialized) {
Logger.e(
this::class.java.simpleName,
"onUpdateListener is not initialized!!,please use fun setOnUpdateListener() to init it"
)
return
}
onUpdateListener.onItemClick(tv_get_sms_code)
}
}
}
/*启动计时器*/
fun startCountDownTimer() {
cancelCountDownTimer()
/*倒计时60秒,每次执行间隔1秒*/
mCountDownTimerUtil = CountDownTimerUtil(mContext, tv_get_sms_code, 60000, 1000)
mCountDownTimerUtil?.start()
}
/*上下文*/
private var mContext: Context
/*存放验证码集合*/
var codes: ArrayList<String>? = ArrayList()
/*输入相关管理器*/
private var imm: InputMethodManager? = null
private var color_default = Color.parseColor("#999999")
private var color_focus = Color.parseColor("#FF9200")
private var color_centerLine = Color.parseColor("#222222")
/*是否显示中间竖线*/
private var isVisibleCenterLine = true
private var defaultInputNum = InputLineNum.SIX
private var mCountDownTimerUtil: CountDownTimerUtil? = null
constructor(context: Context) : super(context) {
mContext = context
initView()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
mContext = context
initView()
}
private fun initView() {
imm = mContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
LayoutInflater.from(mContext).inflate(R.layout.view_new_sms_code_input_layout, this)
initEvent()
}
private fun initEvent() {
//验证码输入
et_code.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable?) {
if (editable != null && editable.trim().isNotEmpty()) {
// 每输入
et_code.setText("")
when (defaultInputNum) {
InputLineNum.FOUR -> {
regexMaxInputSize(editable, InputLineNum.FOUR.num)
}
InputLineNum.SIX -> {
regexMaxInputSize(editable, InputLineNum.SIX.num)
}
}
}
}
})
// 监听验证码删除按键
et_code.setOnKeyListener(object : View.OnKeyListener {
override fun onKey(view: View, keyCode: Int, keyEvent: KeyEvent): Boolean {
if (keyCode == KeyEvent.KEYCODE_DEL && keyEvent.action == KeyEvent.ACTION_DOWN && codes?.size!! > 0) {
codes!!.removeAt(codes?.size!! - 1)
//回退的时候如果顶部的提示语显示则隐藏掉
if (tv_view_top_tip.visibility == View.VISIBLE) {
tv_view_top_tip.visibility = View.INVISIBLE
}
showCode()
return true
}
return false
}
})
tv_get_sms_code.setOnClickListener(this)
}
/*控制可输入的最大长度*/
private fun regexMaxInputSize(editable: Editable, maxSize: Int) {
if (codes?.size!! < maxSize) {
// 过滤掉由空格键引起的字符串出现空长串的问题,使用正则替换规则(\\s*)可以替换掉绝大多数空白字符或空格
//处理掉当用户是复制粘贴验证码时输入框显示问题,转数组遍历的形式处理
val codeList = editable.toString().replace(Regex("\\s*"), "").split("")
codeList.forEach {
if (!TextUtils.isEmpty(it))
codes?.add(it)
}
showCode()
}
}
/**
* 显示输入的验证码
*/
private fun showCode() {
var code1: String? = ""
var code2: String? = ""
var code3: String? = ""
var code4: String? = ""
var code5: String? = ""
var code6: String? = ""
if (codes?.size!! >= 1) {
code1 = codes?.get(0)
}
if (codes?.size!! >= 2) {
code2 = codes?.get(1)
}
if (codes?.size!! >= 3) {
code3 = codes?.get(2)
}
if (codes?.size!! >= 4) {
code4 = codes?.get(3)
}
if (codes?.size!! >= 5) {
code5 = codes?.get(4)
}
if (codes?.size!! >= 6) {
code6 = codes?.get(5)
}
tv_code1.text = code1
tv_code2.text = code2
tv_code3.text = code3
tv_code4.text = code4
tv_code5.text = code5
tv_code6.text = code6
setColor()//设置高亮颜色
callBack()//回调
}
/**
* 设置高亮颜色
*/
private fun setColor() {
r1.setBackgroundResource(R.drawable.shape_verify_sms_item)
r2.setBackgroundResource(R.drawable.shape_verify_sms_item)
r3.setBackgroundResource(R.drawable.shape_verify_sms_item)
r4.setBackgroundResource(R.drawable.shape_verify_sms_item)
r5.setBackgroundResource(R.drawable.shape_verify_sms_item)
r6.setBackgroundResource(R.drawable.shape_verify_sms_item)
if (codes?.size == 0) {
r1.setBackgroundResource(R.drawable.shape_verify_sms_item_select)
updateCenterLineColor(v1_center_line)
}
if (codes?.size == 1) {
r2.setBackgroundResource(R.drawable.shape_verify_sms_item_select)
updateCenterLineColor(v2_center_line)
}
if (codes?.size == 2) {
r3.setBackgroundResource(R.drawable.shape_verify_sms_item_select)
updateCenterLineColor(v3_center_line)
}
if (codes?.size!! == 3) {
r4.setBackgroundResource(R.drawable.shape_verify_sms_item_select)
updateCenterLineColor(v4_center_line)
}
if (codes?.size == 4) {
r5.setBackgroundResource(R.drawable.shape_verify_sms_item_select)
updateCenterLineColor(v5_center_line)
}
if (codes?.size!! == 5) {
r6.setBackgroundResource(R.drawable.shape_verify_sms_item_select)
updateCenterLineColor(v6_center_line)
}
if ((defaultInputNum == InputLineNum.FOUR && codes?.size!! >= 4)
|| (defaultInputNum == InputLineNum.SIX && codes?.size!! >= 6)
) {
invisibleAllCenterLine()
}
}
/**
* 回调
*/
private fun callBack() {
if (!this::onUpdateListener.isInitialized) {
Logger.e(
this::class.java.simpleName,
"onUpdateListener is not initialized!!,please use fun setOnUpdateListener() to init it"
)
return
}
if ((defaultInputNum == InputLineNum.FOUR && codes?.size == 4)
|| (defaultInputNum == InputLineNum.SIX && codes?.size == 6)
) {
/*zi自动收起软键盘*/
dismissSoftInput()
onUpdateListener.onSuccess(getPhoneCode())
} else {
onUpdateListener.onInput()
}
}
/**
* 显示键盘
*/
fun showSoftInput() {
//显示软键盘
KeyboardUtils.showSoftInput(et_code)
}
/**
* 英藏键盘
*/
fun dismissSoftInput() {
KeyboardUtils.hideSoftInput(et_code)
}
/**
* 获得手机号验证码
* @return 验证码
*/
fun getPhoneCode(): String {
val sb = StringBuilder()
return if (!codes?.isNullOrEmpty()!!) {
codes?.let {
for (code in it) {
sb.append(code)
}
}
sb.toString()
} else {
""
}
}
/*更新竖线显示以及颜色*/
private fun updateCenterLineColor(view: View) {
if (isVisibleCenterLine) {
invisibleAllCenterLine()
view.visibility = View.VISIBLE
}
}
/*英藏所有竖线*/
private fun invisibleAllCenterLine() {
v1_center_line.visibility = View.INVISIBLE
v2_center_line.visibility = View.INVISIBLE
v3_center_line.visibility = View.INVISIBLE
v4_center_line.visibility = View.INVISIBLE
v5_center_line.visibility = View.INVISIBLE
v6_center_line.visibility = View.INVISIBLE
}
/*设置顶部提示是否显示*/
fun setTopTipVisible(isVisible: Boolean) {
tv_view_top_tip.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
}
/*设置当前项中间竖线是否显示*/
fun setCurrentCenterLineVisible(isVisible: Boolean) {
isVisibleCenterLine = isVisible
//显示竖线的话默认显示出第一条
v1_center_line.visibility = if (isVisibleCenterLine) View.VISIBLE else View.INVISIBLE
}
/*设置底部获取短信按钮是否显示*/
fun setBottomSmsTvVisible(isVisible: Boolean) {
tv_get_sms_code.visibility = if (isVisible) View.VISIBLE else View.GONE
}
/*设置顶部提示字样*/
fun setTopTipText(text: String) {
tv_view_top_tip.text = text
}
/*设置顶部提示字样颜色*/
fun setTopTipTextColor(textColor: Int) {
tv_view_top_tip.setTextColor(textColor)
}
/*设置当前指定项下环线颜色*/
fun setCurrentIndexLineColor(background: Int) {
r1.setBackgroundResource(R.drawable.shape_verify_sms_item_select)
}
/*设置当前指定项的中间线颜色*/
fun setCenterLineColor(centerLineColor: Int) {
color_centerLine = centerLineColor
v1_center_line.setBackgroundColor(color_centerLine)
}
/*设置不是当前指定项下划线颜色*/
fun setAnotherIndexLineColor(underlineColor: Int) {
color_default = underlineColor
}
/*设置顶部提示的字样和颜色*/
fun setTopTextAndColor(text: String, textColor: Int) {
tv_view_top_tip.text = text
tv_view_top_tip.setTextColor(textColor)
}
/*允许的输入类型*/
fun setInputType(type: Int) {
et_code?.inputType = type
}
/*更新获取短信按钮状态*/
fun updateGetSmsTvEnable(isEnable: Boolean) {
tv_get_sms_code.isEnabled = isEnable
tv_get_sms_code.setTextColor(mContext.resources.getColor(R.color.global_text_color_6c))
}
/*需要展示的输入框数量*/
fun setShowInputNum(num: InputLineNum) {
defaultInputNum = num
when (defaultInputNum) {
InputLineNum.FOUR -> {
r5.visibility = View.GONE
r6.visibility = View.GONE
}
InputLineNum.SIX -> {
r5.visibility = View.VISIBLE
r6.visibility = View.VISIBLE
}
}
}
/*关闭清除计时器*/
fun cancelCountDownTimer() {
if (mCountDownTimerUtil != null) {
mCountDownTimerUtil?.cancel()
mCountDownTimerUtil = null
}
}
/**清除所有输入的值*/
fun clearAllInputValues() {
setTopTipVisible(false)
codes?.clear()
showCode()
}
/**
* 获取到验证码进行弹窗显示
*/
fun showSmsCodeDialogTip(msg: String, title: String) {
val msgSplit = msg.toList()
DialogCreator.createTitleDialog(
mContext as Activity,
title,
msg,
DialogViewInfo("知道了") { _, _ ->
codes?.clear()
msgSplit.forEach { item -> codes?.add(item.toString()) }
showCode()
}
).subscribe()
}
/**
* 验证出错时抖动输入框提示
*/
fun startShakeTip() {
val animX = ObjectAnimator.ofFloat(this, "translationX", 0F, 5F, -10F, 0F)
val animY = ObjectAnimator.ofFloat(this, "translationY", 0F, 5F, -10F, 0F)
AnimatorSet().apply {
playTogether(animX, animY) // 同时执行x、y轴的动画
interpolator = CycleInterpolator(2F)// 执行2次
duration = 500 // 1秒后结束
doOnEnd {
clearAllInputValues()
}
start()
}
}
/*
* 被lateinit修饰的变量判断是否已初始化的方法:
* this::onUpdateListener.isInitialized
* =>为true则是已初始化
*/
private lateinit var onUpdateListener: OnUpdateListener
//定义回调
interface OnUpdateListener {
/**
* 验证码输入完成时执行
*/
fun onSuccess(code: String)
/**
* 点击内部控件时执行,需要先添加click事件
*/
fun onItemClick(view: View)
/**
* 验证码未输入或者输入个数未完成时执行
*/
fun onInput()
}
class ListenerBuilder : OnUpdateListener {
/*
* kotlin的lateinit慎用,如果使用了此修饰,就要确保回调方法每个都需重写,因为这些函数变量都是在此初始化的
* 比如下方的onInputFun方法,其他地方我没有调用此回调方法就会导致onInputField没有被赋值初始化,导致出现崩溃异常
* lateinit property onInputField has not been initialized:
* kotlin.UninitializedPropertyAccessException: lateinit property onInputField has not been initialized
*
* 解决办法
* 可使用被lateinit修饰的变量判断是否已初始化的方法:
* this::onSuccessField.isInitialized
* =>为true则是已初始化
*/
private lateinit var onSuccessField: ((code: String) -> Unit)
private lateinit var onItemClickField: ((view: View) -> Unit)
private lateinit var onInputField: (() -> Unit)
fun onSuccessFun(onSuccessField: (code: String) -> Unit) {
this.onSuccessField = onSuccessField
}
fun onItemClickFun(onItemClickField: (view: View) -> Unit) {
this.onItemClickField = onItemClickField
}
fun onInputFun(onInputField: () -> Unit) {
this.onInputField = onInputField
}
override fun onSuccess(code: String) {
if (this::onSuccessField.isInitialized) onSuccessField.invoke(code)
}
override fun onItemClick(view: View) {
if (this::onItemClickField.isInitialized) onItemClickField.invoke(view)
}
override fun onInput() {
if (this::onInputField.isInitialized) onInputField.invoke()
}
}
/*输入框监听回调《供外部调用》*/
fun setOnUpdateListener(onUpdateListener: ListenerBuilder.() -> Unit) {
val builder = ListenerBuilder()
builder.onUpdateListener()
this.onUpdateListener = builder
}
}
主要有两种显示样式,在枚举中定义了4个输入框6个输入框
基本调用代码如下:
//ll_sms_input就是CustomSmsCodeInputLayout
ll_sms_input.run {
setTopTipVisible(false)
setCurrentCenterLineVisible(true)
setBottomSmsTvVisible(true)
setShowInputNum(NewCustomSmsCodeInputLayout.InputLineNum.SIX)
setCurrentIndexLineColor(resources.getColor(R.color.global_text_color_grey))
//设置输入类型只能是数字
setInputType(InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_SIGNED)
showSoftInput()
}
ll_sms_input.setOnUpdateListener {
onInputFun {
HandleVerifySmsActivityLogic.onInputing()
}
onSuccessFun {
HandleVerifySmsActivityLogic.onInputSuccess(it, clientNo, this@VerifySmsActivity)
}
onItemClickFun {
/*点击获取短信按钮时的回调,在此执行获取短信验证码接口*/
HandleVerifySmsActivityLogic.onGetSmsButtonClick()
}
}