自定义验证码数字键盘

主宰稳场 提交于 2020-01-30 04:33:47

自定义验证码数字键盘

序言

我们会遇到很多输入六位数,四为数的验证码界面

最终效果如下

在这里插入图片描述

问题

那我们做这类界面的时候遇到的什么问题呢

  1. 需要有四个或者六个固定的输入框供用户输入
  2. 输入框内不能有光标,用户只能从后往前删除数字
  3. 如果用edittext那么会一直有光标可以选择,文字输入一个之后跳转到另一个会有一定的bug,包括edittext的聚焦,光标显示在不对的输入框内的问题等等
  4. 如果用原生的键盘,那么有的时候不可避免的用户可以切换到标点符号输入键盘,有的甚至可以到英文键盘,这样需要过滤掉这些不能输入的字符。而且用户切换过去之后不好切回来体验极差
  5. 输入框的唤醒,我们很难很好的去操作输入框的显示和隐藏,这是很蛋疼的代码,可能你要写很多代码才能去隐藏掉原生的输入框,然后在有些机型上可能这个代码并没什么效果。

预期操作方式

那我们预期达到的操作方式是什么呢,我们假设用户是傻子或者好奇心极强,以及故意找漏洞的不怀好意的人。

  1. 我们希望用户只能点击,甚至只能看到数字键盘,连一个标点符号按钮都看不到
  2. 我们希望用户不要去随意的换输入框去输入数字,只能从前往后输入,只能从后往前删除

这样的输入框可能很简单很呆板,但是体验非常好,也避免了不怀好意的用户用出很多问题,因为我们的功能就这么多,功能越多问题越多,那么长痛不如短痛,我们最简单的办法就是学习市面上大多数厂家那样,自定义这整套界面。

实现

那我么实现就分为两个部分,一个是输入框界面,还有一个就是自定义的键盘。

输入框界面思路

  1. 我们根本不需要光标,这会影响我们的原生输入框的弹出和影藏,还会不好控制焦点,那么我们就只需要用TextView来代替
  2. 我们只需要遍历来判断输入框中有没有字,就可以做到按顺序输入的视觉效果
  3. 输入到最后一个的时候可以自动触发发送验证码的操作

键盘界面的思路

1.我们只需要数字0-9这十个数字的输入按钮,以及一个删除按钮
2.我们需要可以弹出隐藏这个键盘,那么底部弹出用popuwindow再好不过了

思路总结

以上便是我提供的所有思路,按照上述描述大部分熟练的人应该可以自己写出来了。

以下贴出部分代码方便大家借鉴

输入框代码

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="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="70dp"
        android:text="请输入6位验证码"
        android:textColor="@color/common_text_color_666" />

    <LinearLayout
        android:id="@+id/input_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:gravity="center">

        <TextView
            android:id="@+id/input1"
            style="@style/TeamInputStyle" />

        <TextView
            android:id="@+id/input2"
            style="@style/TeamInputStyle" />

        <TextView
            android:id="@+id/input3"
            style="@style/TeamInputStyle" />

        <TextView
            android:id="@+id/input4"
            style="@style/TeamInputStyle" />

        <TextView
            android:id="@+id/input5"
            style="@style/TeamInputStyle" />

        <TextView
            android:id="@+id/input6"
            style="@style/TeamInputStyle"
            android:layout_marginEnd="0dp" />
    </LinearLayout>


</LinearLayout>

style

    <style name="TeamInputStyle">
        <item name="android:layout_width">50dp</item>
        <item name="android:layout_height">50dp</item>
        <item name="android:textSize">30sp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textColor">@color/textBlack</item>
        <item name="android:gravity">center</item>
        <item name="android:layout_marginEnd">10dp</item>
        <item name="android:background">@drawable/team_number</item>
    </style>

drawble

//正常
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    <corners android:radius="8dp" />
    <solid android:color="#ffffff" />
    <stroke
        android:width="1dp"
        android:color="#AAA" />
</shape>
//红色
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
    <solid android:color="#ffffff" />
    <corners android:radius="8dp" />
    <stroke
        android:width="1dp"
        android:color="#FF5742" />
</shape>
//灰色
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#ffffff" />
    <corners android:radius="8dp" />
    <stroke
        android:width="1dp"
        android:color="#CC000000" />
</shape>

kotlin代码

    override fun afterTextChanged(s: Editable?) {
        if (s?.length == 1) {//这里只监听了最后一个输入框
            val builder = StringBuilder()
            inputs.forEach {
                builder.append(it.text)
            }
            val params = mutableMapOf(
                    Constant.PASS_KEY to builder.toString()
            )
            //然后就可以把这个params拿去做请求了
        }
    }

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

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

    override fun onInput(text: String) {

        for (input in inputs) {
            if (input.text.isEmpty()) {
                input.text = text
                if (input.id == R.id.input1) {
                    inputs.forEach {
                        it.setBackgroundResource(R.drawable.team_number)
                    }
                }
                input.setBackgroundResource(R.drawable.team_number_gray)
                return
            }
        }
    }

    override fun delete() {
        for (input in inputs.asReversed()) {
            if (input.text.isNotEmpty()) {
                input.text = ""
                return
            }
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_xxx)
        inputPop = TeamInputPopupWindow(this, this)
        title_text.text = "输入验证码"
        inputs = listOf(
                input1,
                input2,
                input3,
                input4,
                input5,
                input6
        )
        input6.addTextChangedListener(this)
        input6.post {
            inputPop.showPop()
        }
    }

以下是TeamInputPopupWindow代码

class TeamInputPopupWindow(private val activity: Activity, private val listener: OnInputListener) :
        View.OnClickListener {


    private lateinit var mPopupWindow: PopupWindow

    fun showPop() {
        val view = LayoutInflater.from(activity)
                .inflate(R.layout.team_input, activity.window.decorView as ViewGroup, false)
        //设置view
        setView(view)
        mPopupWindow = PopupWindow(
                view,
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        )
        mPopupWindow.isFocusable = true
        mPopupWindow.isOutsideTouchable = true
        mPopupWindow.showAtLocation(view, Gravity.BOTTOM, 0, 0)
//        mPopupWindow.showAtLocation(parent, Gravity.BOTTOM, 0, 0)
    }

    fun dismiss() {
        mPopupWindow.dismiss()
    }

    private fun setView(view: View) {
        view.findViewById<View>(R.id.one).setOnClickListener(this)
        view.findViewById<View>(R.id.two).setOnClickListener(this)
        view.findViewById<View>(R.id.three).setOnClickListener(this)
        view.findViewById<View>(R.id.four).setOnClickListener(this)
        view.findViewById<View>(R.id.five).setOnClickListener(this)
        view.findViewById<View>(R.id.six).setOnClickListener(this)
        view.findViewById<View>(R.id.seven).setOnClickListener(this)
        view.findViewById<View>(R.id.eight).setOnClickListener(this)
        view.findViewById<View>(R.id.nine).setOnClickListener(this)
        view.findViewById<View>(R.id.zero).setOnClickListener(this)
        view.findViewById<View>(R.id.delete).setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.one -> listener.onInput("1")
            R.id.two -> listener.onInput("2")
            R.id.three -> listener.onInput("3")
            R.id.four -> listener.onInput("4")
            R.id.five -> listener.onInput("5")
            R.id.six -> listener.onInput("6")
            R.id.seven -> listener.onInput("7")
            R.id.eight -> listener.onInput("8")
            R.id.nine -> listener.onInput("9")
            R.id.zero -> listener.onInput("0")
            R.id.delete -> listener.delete()
        }
    }
}

interface OnInputListener {
    fun onInput(text: String)
    fun delete()
}

以上便是大部分的片段代码了,根据以上逻辑已经可以完成大部分的效果了。

总结

很多东西其实思路比实现更重要,如果你知道怎么实现会少坑的话,尽量在设计阶段就回避掉这些东西。以上便是如何实现验证码输入框的页面逻辑了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!