自定义验证码数字键盘

作者: 171Arios | 来源:发表于2020-01-29 19:29 被阅读0次

    自定义验证码数字键盘

    序言

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

    最终效果如下

    在这里插入图片描述

    问题

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

    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()
    }
    

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

    总结

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

    相关文章

      网友评论

        本文标题:自定义验证码数字键盘

        本文链接:https://www.haomeiwen.com/subject/jxlbthtx.html