由于现在的设计(狗)花样越来多,系统的控件已经满足不了他们膨胀的需求了,所以我们很多时候需要自己去设计控件(自定义控件)。比如现在的主流输入验证码控件,如下图:
初步思考:根据设计图可以看出,每个数字是分离的,当输入一个数字后光标应该要移动到下一个空格里,当删除空格里的数字的时候光标应该移动到前面的空格里,每个空格里只允许有一个数字。那么问题来了。
设计方案有两种:
- 第一种是使用继承View重写onDraw来画空格布局,但是光标的控制处理比较麻烦,优点是布局结构简单对系统性能友好。
- 第二种使用组合控件,即:使用四个EditText外部包裹LineaLayout,这种方式处理比较简单,我们不用太过操心光标和文本输入,而且每个Item的样式可以随意修改。不过这样导致控件体积变大,对性能不太友好。
所以我选择第二种(没错,我就是懒)。
下面开始我的常规操作,直接上代码不多BB。
组合布局widget_code_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/v_code1"
android:maxLength="1"
android:inputType="number"
android:focusable="true"
android:gravity="center"
android:background="@drawable/shape_code_bg"
android:layout_width="50dp"
android:layout_height="50dp"/>
<View
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="0dp"/>
<EditText
android:id="@+id/v_code2"
android:inputType="number"
android:gravity="center"
android:background="@drawable/shape_code_bg"
android:layout_width="50dp"
android:layout_height="50dp"/>
<View
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="0dp"/>
<EditText
android:inputType="number"
android:id="@+id/v_code3"
android:gravity="center"
android:background="@drawable/shape_code_bg"
android:layout_width="50dp"
android:layout_height="50dp"/>
<View
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="0dp"/>
<EditText
android:id="@+id/v_code4"
android:gravity="center"
android:inputType="number"
android:background="@drawable/shape_code_bg"
android:layout_width="50dp"
android:layout_height="50dp"/>
</LinearLayout>
Java代码部分:
package com.zhongde.haokuai.widget
import android.content.Context
import android.text.Editable
import android.text.InputFilter
import android.text.TextWatcher
import android.util.AttributeSet
import android.util.Log
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.EditText
import android.widget.LinearLayout
import com.zhongde.haokuai.R
import kotlinx.android.synthetic.main.widget_code_view.view.*
/**
* @package
* @anthor luan
* @date 2019/3/1
* @des 验证码控件
*/
class VerifyCodeView : LinearLayout {
constructor(context: Context?) : super(context) {
initView(context)
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initView(context)
}
lateinit var inflate: ViewGroup
//输入完成回调
var onInputComplete: (code: String) -> Unit = {}
//输入不完整
var onInputIncomplete: () -> Unit = {}
//布局初始化
private fun initView(context: Context?) {
inflate = LayoutInflater.from(context).inflate(R.layout.widget_code_view, null) as ViewGroup
addView(inflate)
//限制长度
v_code1.filters = arrayOf(InputFilter.LengthFilter(1))
v_code2.filters = arrayOf(InputFilter.LengthFilter(1))
v_code3.filters = arrayOf(InputFilter.LengthFilter(1))
v_code4.filters = arrayOf(InputFilter.LengthFilter(1))
initEvent(context)
}
//事件初始化
private fun initEvent(context: Context?) {
//监听edittext内容变化
v_code1.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
//如果输入长度等于1,将焦点交给下一个控件
if (s.isNotEmpty()) {
v_code2.requestFocus()
}
if (s.isEmpty())
onInputIncomplete()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
v_code2.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
//如果输入长度等于1,将焦点交给下一个控件
if (s.isNotEmpty()) {
v_code3.requestFocus()
}
if (s.isEmpty())
onInputIncomplete()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
v_code2.setOnKeyListener { v, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_DEL && (v as EditText).text.isEmpty()) {
v_code1.requestFocus()
}
return@setOnKeyListener false
}
v_code3.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
//如果输入长度等于1,将焦点交给下一个控件
if (s.isNotEmpty()) {
v_code4.requestFocus()
}
if (s.isEmpty())
onInputIncomplete()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
v_code3.setOnKeyListener { v, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_DEL && (v as EditText).text.isEmpty()) {
v_code2.requestFocus()
}
return@setOnKeyListener false
}
v_code4.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
//如果输入长度等于1,输入完成
if (s.isNotEmpty()) {
val t1 = v_code1.text.toString().trim()
val t2 = v_code2.text.toString().trim()
val t3 = v_code3.text.toString().trim()
val t4 = v_code4.text.toString().trim()
onInputComplete(t1 + t2 + t3 + t4)
}
if (s.isEmpty())
onInputIncomplete()
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
v_code4.setOnKeyListener { v, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_DEL && (v as EditText).text.isEmpty()) {
v_code3.requestFocus()
}
return@setOnKeyListener false
}
}
fun getCode(): String {
val t1 = v_code1.text.toString().trim()
val t2 = v_code2.text.toString().trim()
val t3 = v_code3.text.toString().trim()
val t4 = v_code4.text.toString().trim()
return t1 + t2 + t3 + t4
}
}
布局中基本使用方式:
<com.zhongde.haokuai.widget.TimingTextView
android:id="@+id/v_send_code"
android:layout_width="wrap_content"
android:textColor="@color/send_code"
android:layout_height="wrap_content"/>
代码中直接获取控件后调用getCode()就能获取到用户输入的验证码了。