其实用官方自定的那个inputEditText默认带下划线的,然后自己再实行焦点和输入框弹出等操作也可以。
写这个自定义View主要是为了练习,当前自定义View必须得给个高度,不能自适应,有需要的朋友可以继续进一步调试。
/**
* 实现了粘贴事件监听回调的 EditText
*/
open class ListenPasteEditTextTest : AppCompatEditText {
constructor(context: Context): super(context)
constructor(context: Context, attributeSet: AttributeSet): super(context,attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int): super(context, attributeSet, defStyleAttr)
var lisenter: ClipInterface ? = null
override fun onTextContextMenuItem(id: Int): Boolean {
when(id) {
//剪切复制黏贴
android.R.id.cut -> lisenter?.onCut();
android.R.id.copy -> lisenter?.onCopy();
android.R.id.paste -> lisenter?.onPaste();
}
return super.onTextContextMenuItem(id)
}
}
interface ClipInterface{
fun onCut()
fun onCopy()
fun onPaste()
}
/**
* 手机验证码输入控件
*/
class VerificationCodeInputTest(context: Context, attributeSet: AttributeSet) : ViewGroup(context, attributeSet), ClipInterface{
private val box = 4
private val boxWidth = 120
private val boxHeight = 120
private var childPadding = 14
private val TYPE_NUMBER = "number"
private val TYPE_TEXT = "text"
private val TYPE_PASSWORD = "password"
private val TYPE_PHONE = "phone"
private val boxBgFocus: Drawable? = null
private val boxBgNormal: Drawable? = null
private val inputType = TYPE_NUMBER
var listener: VerCideListener? = null
init {
val textWatcher = 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?) {
if (s != null) {
if (s.isNotEmpty()) {
focus()
checkAndCommit()
}
}
}
}
val onKeyListener = OnKeyListener { v, keyCode, event ->
if (keyCode == KeyEvent.KEYCODE_DEL) {
//backFocus();
backFocusClearAll()
}
false
}
//四个输入框
for (index in 0 until box) {
val editText = ListenPasteEditTextTest(context)
val layoutParams = LinearLayout.LayoutParams(boxWidth, boxHeight)
layoutParams.bottomMargin = childPadding
layoutParams.topMargin = childPadding
layoutParams.leftMargin = childPadding
layoutParams.rightMargin = childPadding
layoutParams.gravity = Gravity.CENTER
editText.layoutParams = layoutParams
editText.lisenter = this
editText.setOnKeyListener(onKeyListener)
//设置背景颜色,就是输入框中的下划线
setBg(editText, false)
editText.setTextColor(Color.BLACK)
editText.gravity = Gravity.CENTER
//最多给你输入一个字符
editText.filters = arrayOf<InputFilter>(InputFilter.LengthFilter(1))
//设置textView输入内容的显示模式
if (TYPE_NUMBER == inputType) {
editText.inputType = InputType.TYPE_CLASS_NUMBER
} else if (TYPE_PASSWORD == inputType) {
editText.transformationMethod = PasswordTransformationMethod.getInstance()
} else if (TYPE_TEXT == inputType) {
editText.inputType = InputType.TYPE_CLASS_TEXT
} else if (TYPE_PHONE == inputType) {
editText.inputType = InputType.TYPE_CLASS_PHONE
}
editText.id = index
//设置字符宽度
editText.setEms(1)
editText.addTextChangedListener(textWatcher)
addView(editText, index)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var parentWidth = measuredWidth
//如果在xml中配置的是match_patent 则直接获取当前手机的width尺寸
if (parentWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
parentWidth = getScreenWidth()
}
Log.d(javaClass.name, "onMeasure width $parentWidth")
val count = childCount
for (i in 0 until count) {
val child = getChildAt(i)
this.measureChild(child, widthMeasureSpec, heightMeasureSpec)
}
if (count > 0) {
val child = getChildAt(0)
val cWidth = child.measuredWidth
if (parentWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
// 重新计算padding
childPadding = (parentWidth - cWidth * count) / (count + 1)
}
val cHeight = child.measuredHeight
val maxH = cHeight + 2 * childPadding
val maxW = cWidth * count + childPadding * (count + 1)
//上面都是计算当前editText的width加上pandding,之后设置给父布局
setMeasuredDimension(
View.resolveSize(maxW, widthMeasureSpec),
View.resolveSize(maxH, heightMeasureSpec)
)
}
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
val childCount = childCount
for (i in 0 until childCount) {
val child = getChildAt(i)
child.visibility = View.VISIBLE
val cWidth = child.measuredWidth
val cHeight = child.measuredHeight
val cl = childPadding + i * (cWidth + childPadding)
val cr = cl + cWidth
val ct = childPadding
val cb = ct + cHeight
child.layout(cl, ct, cr, cb)
}
}
private fun getScreenWidth(): Int {
val resources = this.resources
val dm = resources.displayMetrics
return dm.widthPixels
}
override fun onCut() {
}
override fun onCopy() {
}
override fun onPaste() {
val copyText = getCutAndCopyText()
// 如果是数字并且 length 和 填写位数一致才会进行填充
if (isNumeric(copyText) && copyText.length == box) {
for (i in 0 until childCount) {
(getChildAt(i) as EditText).append(copyText.get(i).toString())
}
}
}
fun setBg(editText: EditText, focus: Boolean) {
if (boxBgNormal != null && !focus) {
editText.background = boxBgNormal
} else if (boxBgFocus != null && focus) {
editText.background = boxBgFocus
}
}
private fun focus() {
val count = childCount
var editText: EditText
for (i in 0 until count) {
editText = getChildAt(i) as EditText
if (editText.text.isEmpty()) {
editText.requestFocus()
return
}
}
}
private fun checkAndCommit() {
val stringBuilder = StringBuilder()
var full = false
for (i in 0 until box) {
val editText = getChildAt(i) as EditText
val content = editText.text.toString()
if (!content.isEmpty()) {
stringBuilder.append(content)
}
}
if (stringBuilder.length == box) {
full = true
}
if (full) {
if (listener != null) {
listener?.onComplete(stringBuilder.toString())
backFocusClearAll()
}
}
}
//清空所有并重新输入
fun backFocusClearAll() {
var editText: EditText
for (i in 0 until box) {
editText = getChildAt(i) as EditText
editText.setText("")
editText.clearFocus()
}
getChildAt(0).requestFocus()
}
/**
* 判断是否是数字
*
* @param str
* @return
*/
private fun isNumeric(str: String?): Boolean {
if (str == null || str.isEmpty()) {
return false
}
for (i in 0 until str.length) {
if (!Character.isDigit(str[i])) {
return false
}
}
return true
}
/**
* 获取剪贴板内容
*/
private fun getCutAndCopyText(): String {
val manager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
if (manager != null && manager.hasPrimaryClip() && manager.primaryClip!!.itemCount > 0) {
val addedText = manager.primaryClip!!.getItemAt(0).text
if (addedText != null) {
return addedText.toString()
}
}
return ""
}
}
interface VerCideListener {
fun onComplete(content: String)
}