Android自定义密码&验证码输入框

2018-11-13  本文已影响0人  刺客的幻影

效果图:

cell_edittext.gif

支持功能:

下面主要讲一下自定义输入框的实现思路:

本来产品的要求是实现一个验证码输入框,主流的验证码都是纯数字组成,所以刚开始想到用自定义数字键盘和输入框来实现,也就有了这篇文章https://www.jianshu.com/p/6c51be7a8371
但是,产品之后又提出另外一个需求,要求能输入密码。这个不仅要支持数字,还要支持各种文本,符号。所以还是老老实实用系统自带的键盘吧。想到的解决办法就是在输入框上面盖上一个透明的EditText来与键盘交互。通过监听键盘的输入达到输入框、EditText的联动。

实现步骤:

  1. 自定义属性:
    isPassword 输入类型是否为密码(因为设计图上密码效果和Android自带效果不一致,需要自定义,单独拿出来与inputType区分)
    textColor 字体颜色
    textSize 字体大小
    inputType 输入类型 (支持number、text)
 <declare-styleable name="CellInputView">
        <attr name="isPassword" format="boolean" />
        <attr name="textColor" format="color|reference" />
        <attr name="textSize" format="float" />
        <attr name="inputType" format="enum">
            <enum name="number" value="0x00000002" />
            <enum name="text" value="0x00000001" />
        </attr>
    </declare-styleable>

2.布局,使用透明的EditText覆盖到单个的输入框上

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:id="@+id/ll_verity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_pay1"
            style="@style/CellInputStyle" />

        <TextView
            android:id="@+id/tv_pay2"
            style="@style/CellInputStyle" />

        <TextView
            android:id="@+id/tv_pay3"
            style="@style/CellInputStyle" />

        <TextView
            android:id="@+id/tv_pay4"
            style="@style/CellInputStyle" />

        <TextView
            android:id="@+id/tv_pay5"
            style="@style/CellInputStyle" />

        <TextView
            android:id="@+id/tv_pay6"
            style="@style/CellInputStyle" />

    </LinearLayout>

    <EditText
        android:id="@+id/et_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent"
        android:cursorVisible="false"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:imeOptions="actionNext"
        android:inputType="text"
        android:maxLength="6"
        android:textColor="@android:color/transparent" />

</RelativeLayout>



 <style name="CellInputStyle">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginStart">16dp</item>
        <item name="android:background">@drawable/input_cell_bg</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">#396AFC</item>
        <item name="android:textSize">20dp</item>
    </style>
  1. 实现逻辑
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        View.inflate(context, R.layout.input_cell_layout, this)
        var textColor = Color.parseColor("#396AFC")
        var textSize = 16f
        attrs?.let {
            context.obtainStyledAttributes(attrs, R.styleable.CellInputView)?.let {
                isPassword = it.getBoolean(R.styleable.CellInputView_isPassword, false)
                textColor = it.getColor(R.styleable.CellInputView_textColor, Color.parseColor("#1B1B4E"))
                textSize = it.getFloat(R.styleable.CellInputView_textSize, 16f)
                inputType = it.getInt(R.styleable.CellInputView_inputType, EditorInfo.TYPE_CLASS_NUMBER)
                it.recycle()
            }
        }

        val width = (context.resources.displayMetrics.widthPixels - UIUtils.dp2px(context, 16f) * 7) / 6
        for (i in 0 until ll_verity.childCount) {
            val child = ll_verity.getChildAt(i)
            val layoutParams = child.layoutParams
            layoutParams.width = width
            layoutParams.height = width
            child.layoutParams = layoutParams
            textViews[i] = child as TextView
            textViews[i]?.setTextColor(textColor)
            textViews[i]?.textSize = textSize
        }
        val layoutParams = et_input.layoutParams as LayoutParams
        layoutParams.height = width
        et_input.layoutParams = layoutParams
        et_input.inputType = inputType
        textViews[0]?.isSelected = true
        setEditTextListener()
    }
 interface InputCompleteListener {
        fun inputComplete()
    }
fun setInputCompleteListener(inputCompleteListener: InputCompleteListener) {
        this.inputCompleteListener = inputCompleteListener
    }
  et_input.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                inputContent = et_input.text.toString()
                if (inputContent.length >= number) {
                    inputCompleteListener?.inputComplete()
                } else {
                    inputCompleteListener?.invalidContent()
                }
                for (i in 0 until number) {
                    textViews[i]?.isSelected = false
                    if (i < inputContent.length) {
                        textViews[i]?.text = if (isPassword) "●" else inputContent[i].toString()
                    } else {
                        textViews[i]?.text = ""
                    }
                }
                when {
                    number - 1 <= inputContent.length -> textViews[5]?.isSelected = true
                    inputContent.isEmpty() -> textViews[0]?.isSelected = true
                    else -> textViews[inputContent.length]?.isSelected = true
                }
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            }

        })
package com.po1arbear.custom.celledittext

import android.annotation.SuppressLint
import android.content.Context

import android.graphics.Color
import android.support.v4.content.ContextCompat
import android.text.Editable
import android.text.TextWatcher
import android.text.method.DigitsKeyListener
import android.util.AttributeSet
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.RelativeLayout
import android.widget.TextView
import kotlinx.android.synthetic.main.input_cell_layout.view.*

class CellInputView : RelativeLayout {

    private val number = 6
    private val textViews: Array<TextView?> = arrayOfNulls(number)
    private var inputContent: String = ""
    private var isPassword = false
    private var inputType = 0

    constructor (context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    @SuppressLint("Recycle")
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        View.inflate(context, R.layout.input_cell_layout, this)
        var textColor = Color.parseColor("#396AFC")
        var textSize = 16f
        attrs?.let {
            context.obtainStyledAttributes(attrs, R.styleable.CellInputView)?.let {
                isPassword = it.getBoolean(R.styleable.CellInputView_isPassword, false)
                textColor = it.getColor(R.styleable.CellInputView_textColor, Color.parseColor("#1B1B4E"))
                textSize = it.getFloat(R.styleable.CellInputView_textSize, 16f)
                inputType = it.getInt(R.styleable.CellInputView_inputType, EditorInfo.TYPE_CLASS_NUMBER)
                it.recycle()
            }
        }

        val width = (context.resources.displayMetrics.widthPixels - UIUtils.dp2px(context, 16f) * 7) / 6
        for (i in 0 until ll_verity.childCount) {
            val child = ll_verity.getChildAt(i)
            val layoutParams = child.layoutParams
            layoutParams.width = width
            layoutParams.height = width
            child.layoutParams = layoutParams
            textViews[i] = child as TextView
            textViews[i]?.setTextColor(textColor)
            textViews[i]?.textSize = textSize
        }
        val layoutParams = et_input.layoutParams as LayoutParams
        layoutParams.height = width
        et_input.layoutParams = layoutParams
        et_input.inputType = inputType
        textViews[0]?.isSelected = true
        setEditTextListener()
    }

    private fun setEditTextListener() {
        et_input.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {
                inputContent = et_input.text.toString()
                if (inputContent.length >= number) {
                    inputCompleteListener?.inputComplete()
                }
                for (i in 0 until number) {
                    textViews[i]?.isSelected = false
                    if (i < inputContent.length) {
                        textViews[i]?.text = if (isPassword) "●" else inputContent[i].toString()
                    } else {
                        textViews[i]?.text = ""
                    }
                }
                when {
                    number - 1 <= inputContent.length -> textViews[5]?.isSelected = true
                    inputContent.isEmpty() -> textViews[0]?.isSelected = true
                    else -> textViews[inputContent.length]?.isSelected = true
                }
            }

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {

            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {

            }

        })


        et_input.setOnEditorActionListener(TextView.OnEditorActionListener { v, actionId, _ ->
            if (actionId == EditorInfo.IME_ACTION_NEXT) {
                inputContent = et_input.text.toString()
                if (inputContent.length >= number) {
                    inputCompleteListener?.inputComplete()
                }
                return@OnEditorActionListener true
            }
            false
        })

    }

    fun clearContent() {
        et_input.setText("")
    }

    private var inputCompleteListener: InputCompleteListener? = null

    fun setInputCompleteListener(inputCompleteListener: InputCompleteListener) {
        this.inputCompleteListener = inputCompleteListener
    }

    interface InputCompleteListener {

        fun inputComplete()

        fun invalidContent() {}
    }

    fun setDigitsKeyListener(digitsKeyListener: DigitsKeyListener) {
        et_input.keyListener = digitsKeyListener
    }

    fun setIsPassword(isPassword: Boolean) {
        this.isPassword = isPassword
    }

    fun setTextColor(color: Int) {
        textViews.forEach { it?.setTextColor(ContextCompat.getColor(context, color)) }
    }

    fun getEditContent(): String {
        return inputContent
    }

    fun showKeyboard() {
        et_input.isFocusable = true
        et_input.isFocusableInTouchMode = true
        et_input.requestFocus()
        val inputManager = context
                .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        inputManager.showSoftInput(et_input, 0)
    }

    fun hideKeyboard() {
        val inputManager: InputMethodManager? =
                context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        inputManager?.hideSoftInputFromWindow(windowToken, 0)
    }


}

项目地址:https://github.com/po1arbear/CellEditText.git

上一篇 下一篇

猜你喜欢

热点阅读