自定义验证码数字键盘
序言
我们会遇到很多输入六位数,四为数的验证码界面
最终效果如下
问题
那我们做这类界面的时候遇到的什么问题呢
- 需要有四个或者六个固定的输入框供用户输入
- 输入框内不能有光标,用户只能从后往前删除数字
- 如果用edittext那么会一直有光标可以选择,文字输入一个之后跳转到另一个会有一定的bug,包括edittext的聚焦,光标显示在不对的输入框内的问题等等
- 如果用原生的键盘,那么有的时候不可避免的用户可以切换到标点符号输入键盘,有的甚至可以到英文键盘,这样需要过滤掉这些不能输入的字符。而且用户切换过去之后不好切回来体验极差
- 输入框的唤醒,我们很难很好的去操作输入框的显示和隐藏,这是很蛋疼的代码,可能你要写很多代码才能去隐藏掉原生的输入框,然后在有些机型上可能这个代码并没什么效果。
预期操作方式
那我们预期达到的操作方式是什么呢,我们假设用户是傻子或者好奇心极强,以及故意找漏洞的不怀好意的人。
- 我们希望用户只能点击,甚至只能看到数字键盘,连一个标点符号按钮都看不到
- 我们希望用户不要去随意的换输入框去输入数字,只能从前往后输入,只能从后往前删除
这样的输入框可能很简单很呆板,但是体验非常好,也避免了不怀好意的用户用出很多问题,因为我们的功能就这么多,功能越多问题越多,那么长痛不如短痛,我们最简单的办法就是学习市面上大多数厂家那样,自定义这整套界面。
实现
那我么实现就分为两个部分,一个是输入框界面,还有一个就是自定义的键盘。
输入框界面思路
- 我们根本不需要光标,这会影响我们的原生输入框的弹出和影藏,还会不好控制焦点,那么我们就只需要用TextView来代替
- 我们只需要遍历来判断输入框中有没有字,就可以做到按顺序输入的视觉效果
- 输入到最后一个的时候可以自动触发发送验证码的操作
键盘界面的思路
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()
}
以上便是大部分的片段代码了,根据以上逻辑已经可以完成大部分的效果了。
总结
很多东西其实思路比实现更重要,如果你知道怎么实现会少坑的话,尽量在设计阶段就回避掉这些东西。以上便是如何实现验证码输入框的页面逻辑了。
来源:CSDN
作者:arios171
链接:https://blog.csdn.net/arios171/article/details/104109006