淘先锋技术网

首页 1 2 3 4 5 6 7

由于现在的设计(狗)花样越来多,系统的控件已经满足不了他们膨胀的需求了,所以我们很多时候需要自己去设计控件(自定义控件)。比如现在的主流输入验证码控件,如下图:
在这里插入图片描述
初步思考:根据设计图可以看出,每个数字是分离的,当输入一个数字后光标应该要移动到下一个空格里,当删除空格里的数字的时候光标应该移动到前面的空格里,每个空格里只允许有一个数字。那么问题来了。
设计方案有两种:

  • 第一种是使用继承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()就能获取到用户输入的验证码了。