过滤无效字符的密码输入框(EditText)

需求:
1、密码由 字母、数字、符号组成。
2、长度为6-16位
3、符号可有可无,字母和数字必须都有,字母不限制大小写(即:不可以纯字母、纯数字、纯符号)
4、如果用户输入无效字符,直接不展示,并且有对应提示

匹配密码,肯定要用到正则,这里推荐个

https://vimsky.com/article/3603.html

实现:
特别留意下最后的说明,有些细节要注意

0、完整的正则检查匹配

        //最少有一个字母和数字,可以有符号。
        //不限制上述三种字符的何种开头
        var s = "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d$@$!%*#?&]{6,16}$"

        var str = ""

        val p = Pattern.compile(s)
        val m = p.matcher(str)

        if(m.find()){

        }else{

        }

1、对实时输入内容(字符)的有效、无效检查,只需要在TextWatcher中做处理
说明:
TextWatcher中,三个核心方法的执行顺序为

TextWatcher: beforeTextChanged
TextWatcher: onTextChanged
TextWatcher: afterTextChanged

自定义一个TextWatcher

import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import java.util.regex.Pattern

class MyTextWatcher : TextWatcher {

    //单个字符检查
    private var sc = "[0-9a-zA-Z{}#%^*+=_/|~<>\$¥,.?!:;()@&]{1}$"

    private var inputCount = 0
    private var startIndex = 0

    private var pc = Pattern.compile(sc)

    override fun afterTextChanged(s: Editable?) {

        try {

            if (s?.toString().isNullOrEmpty().not() && startIndex <= s.toString().length && startIndex + inputCount <= s.toString().length) {

                var c: String = s.toString()[startIndex].toString()

                if (!pc.matcher(c).find()) {
                    s?.delete(startIndex, startIndex + inputCount)
                    if (c.equals(" ")) {
                        Log.e("afterTextChanged  ", "空格为无效字符")
                    } else {
                        Log.e("afterTextChanged  ", "${c}为无效字符")
                    }
                }
            }

        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

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

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

2、使用:

    <EditText
            android:id="@+id/pwd_et"
            android:textSize="20dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textPassword"
    />

界面中
pwd_et.addTextChangedListener(MyTextWatcher())

特别说明:
1、onTextChanged中,参数说明:
模拟情况:现在已经输入了1234,将光标定位到2和3之间,输入5
打印结果为:

 s is 12534
start is 2
count is 1

由此可知,s ,是总内容;start,是最后一次输入文字时,被输入内容在s中的起始位置;count,是输入的文字的长度

还可以去掉布局中的android:inputType=“textPassword”,用表情和颜文字等进行验证。这个,请自行验证。

基于此,在afterTextChanged方法中,拿到当次输入的内容,对其进行判断,看是否是需要的字符,如果不是,就视其为“无效字符”,将其删掉

2、引申问题,既然可能输入颜文字,那么,颜文字 的长度很长,为什么afterTextChanged方法中,没有对 inputCount >1 的情况进行判断。

理由:
1、既然是密码输入框,肯定会有属性

android:inputType="textPassword"

有了这句话,软键盘出现后,不会有“表情”和“颜文字”的选项;而且,就算是从其他复制进来的,也不会造成异常,因为最后点击“确认”按钮,肯定还会对整体进行一次正则校验

2、会过滤掉用户复制、粘贴的内容

3、如果对于 inputCount >1 的情况进行判断处理(处理方式后面会说到),在密文和明文之间切换时,会有崩溃异常或清空已输入内容

关于崩溃,在

<com.google.android.material.textfield.TextInputLayout
				......
				>
			<EditText
					......
					>
</com.google.android.material.textfield.TextInputLayout>

这种布局下,必现,有兴趣的可以复现下

现对 inputCount >1 引起内容清空问题做特别解释

首先,要知道,引起、造成 inputCount >1 的情况,有哪些。
无非两种:1、复制、粘贴;2、输入表情、颜文字等

用户输入密码,肯定是一位一位的输入,那么inputCount 应该是1。如果大于1,就说明是 一次性输入了不合理的内容(无法确认,用户是复制了密码,还是复制了其他东西,或者输入了颜文字)。因为情况很多,且无法对其监听,只能统一做删除处理。

将上述代码改为:

    override fun afterTextChanged(s: Editable?) {

        try {

            if (s?.toString().isNullOrEmpty().not() && startIndex <= s.toString().length && startIndex + inputCount <= s.toString().length) {

                if (inputCount > 1) {
                    s?.delete(startIndex, startIndex + inputCount)
                    Log.e("afterTextChanged  ", "无效字符")
                } else {

                    var c: String = s.toString()[startIndex].toString()

                    if (!pc.matcher(c).find()) {
                        s?.delete(startIndex, startIndex + inputCount)
                        if (c.equals(" ")) {
                            Log.e("afterTextChanged  ", "空格为无效字符")
                        } else {
                            Log.e("afterTextChanged  ", "${c}为无效字符")
                        }
                    }

                }
            }

        } catch (e: Exception) {
            e.printStackTrace()
        }

    }

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

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

对 输入内容大于 1 的情况做了 删除的处理

如果用户复制了一段文字,因为count>1,就直接被清理掉了

现在,再看第二种情况:已输入内容被清空
模拟情况为:输入内容12345(根据布局中的设置,默认是密文形式),清空之前所有日志,现在,点击按钮,切换为明文

tv_1.setOnClickListener {
	//展示明文
	pwd_et.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
}

tv_2.setOnClickListener {
	//展示密文
	pwd_et.setTransformationMethod(PasswordTransformationMethod.getInstance());
}

打印日志为:

onTextChanged:  s is 12345
onTextChanged:  start is 0
onTextChanged:  count is 5
afterTextChanged:  startIndex is 0
afterTextChanged:  inputCount is 5

onTextChanged:  s is 
onTextChanged:  start is 0
onTextChanged:  count is 0
afterTextChanged:  startIndex is 0
afterTextChanged:  inputCount is 0
afterTextChanged: 无效字符

可以看到,TextWatcher被执行了两次。
第一次,拿到了 已输入 内容,因为count>1,被清理掉了。
第二次,只能拿到空

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值