Android验证码输入框的实现!!!

前言

验证码输入框是很多APP必不可少的组件,之前在重构注册登录页面的时候,重新设计了UI,所以不能再简单的用EditText来做了,所以这篇文章将分享一下如何实现一个常见的验证码输入框。

正文

重点在输入框,可能大多数APP里都是采用6个方框的UI效果,我这里是按照我们设计的要求,用6根横线来划出6个数字的位置。一开始我想的是直接用6个TextView,然后传递焦点的做法,但是发现实现起来有一定的难度。又在网上查了一下,发现比较靠谱的办法是用6个TextView加一个EditText来实现,也按照这个方法去实现了,但是后来在测试的时候就发现了问题:网上给出的实现方式需要监听软键盘的删除按钮

editText.setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_DEL
                        && event.getAction() == KeyEvent.ACTION_DOWN) {
                    //TODO:
                    return true;
                }
                return false;
            }
        });

这是一个大家熟知的写法,但是这个监听的方法其实并不靠谱(在安卓原生键盘上就监听不到),因为这个监听是否触发,并没有强制的要求,全看输入法开发者的心情,这是官方文档中的描述:

Key presses in software keyboards will generally NOT trigger this method, although some may elect to do so in some situations.

只能输入,不能删除,这可不行啊,用户肯定会骂娘的,我可不想被拿去去祭天什么的…
于是乎只能想办法在原有的基础上做一些修改,来规避这个问题,最后采用的方案是:采用一个TextView的数组来维护6个TextView,然后藏一个透明的EditTextView在后面用于接收用户输入的内容,再把输入的内容展示到6个TextView上就行了,UI什么的可以自己随意设计。在实现的过程中,遇到的一个关键问题就是:当输入的内容超过6位以后我该如何处理?一开始的方案是通过判断当前输入的位数然后再做相应的处理,网上的方案也是这么实现的,我后来一想,根本用不着这么麻烦,只需要一行属性就能解决这个问题:

android:maxLength="6"

只需要在EditText的属性里限制它的最大长度,就不用再去代码里做处理了,直接把EditTextView里的内容完全照搬到TextView上就可以了。
最终的完整代码如下:

public class VerifyCodeView extends RelativeLayout {
    private EditText editText;
    private TextView[] textViews;
    private static int MAX = 6;
    private String inputContent;

    public VerifyCodeView(Context context) {
        this(context, null);
    }

    public VerifyCodeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VerifyCodeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        View.inflate(context, R.layout.view_verify_code, this);

        textViews = new TextView[MAX];
        textViews[0] = (TextView) findViewById(R.id.tv_0);
        textViews[1] = (TextView) findViewById(R.id.tv_1);
        textViews[2] = (TextView) findViewById(R.id.tv_2);
        textViews[3] = (TextView) findViewById(R.id.tv_3);
        textViews[4] = (TextView) findViewById(R.id.tv_4);
        textViews[5] = (TextView) findViewById(R.id.tv_5);
        editText = (EditText) findViewById(R.id.edit_text_view);

        editText.setCursorVisible(false);//隐藏光标
        setEditTextListener();
    }

    private void setEditTextListener() {
        editText.addTextChangedListener(new TextWatcher() {

            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void afterTextChanged(Editable editable) {
                inputContent = editText.getText().toString();

                if (inputCompleteListener != null) {
                    if (inputContent.length() >= MAX) {
                        inputCompleteListener.inputComplete();
                    } else {
                        inputCompleteListener.invalidContent();
                    }
                }

                for (int i = 0; i < MAX; i++) {
                    if (i < inputContent.length()) {
                        textViews[i].setText(String.valueOf(inputContent.charAt(i)));
                    } else {
                        textViews[i].setText("");
                    }
                }
            }
        });
    }


    private InputCompleteListener inputCompleteListener;

    public void setInputCompleteListener(InputCompleteListener inputCompleteListener) {
        this.inputCompleteListener = inputCompleteListener;
    }

    public interface InputCompleteListener {

        void inputComplete();

        void invalidContent();
    }

    public String getEditContent() {
        return inputContent;
    }

}

经过thisfeng的提醒,发现存在几个问题:

1.双击和长按会选中EditText的内容,出现复制粘贴等选项

2.光标位置会随着点击而改变,输入数字可能会插入到中间的位置

于是做了相应的修改:

//屏蔽长按事件
android:longClickable="false"

使用自定义EditText:
public class MyEditText extends AppCompatEditText {

    private long lastTime = 0;

    public MyEditText(Context context) {
        super(context);
    }

    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onSelectionChanged(int selStart, int selEnd) {
        super.onSelectionChanged(selStart, selEnd);
        //把光标位置固定在最末
        this.setSelection(this.getText().length());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //屏蔽双击事件
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                long currentTime = System.currentTimeMillis();
                if (currentTime - lastTime < 500) {
                    lastTime = currentTime;
                    return true;
                } else {
                    lastTime = currentTime;
                }
                break;
        }
        return super.onTouchEvent(event);
    }
}

结语

有时候我们实现一个需求,不光要考虑最终的效果,还要考虑时间成本,能用最简单的方法实现当然是最好的,省下的时间拿来打把昆特牌也是不错的。而且写的代码越少,出错的几率越低嘛,是不是~ 好了今天就分享到这里,我要去熬我的西米露了。如有错误,欢迎大家指正

最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义View,动画实现,架构分享等。 内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。 大家可以跟我一起探讨,欢迎加群探讨,有flutter—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿~ 群号:661841852

点我加入群聊【Android开发行业交流】

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您可以使用以下代码来实现点击显示密码开关时,将密码输入框验证码输入框中的输入类型显示为可见: ```java // 获取密码输入框验证码输入框 EditText passwordEditText = findViewById(R.id.password_edit_text); EditText codeEditText = findViewById(R.id.code_edit_text); // 获取密码开关按钮 ToggleButton showPasswordToggleButton = findViewById(R.id.show_password_toggle_button); // 设置密码开关按钮的监听器 showPasswordToggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // 如果密码开关按钮被选中,则显示密码 if (isChecked) { passwordEditText.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); codeEditText.setTransformationMethod(HideReturnsTransformationMethod.getInstance()); } // 如果密码开关按钮没有被选中,则隐藏密码 else { passwordEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); codeEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); } } }); ``` 上述代码中,我们获取了密码输入框验证码输入框以及密码开关按钮,并设置了密码开关按钮的监听器。在密码开关按钮被选中时,我们将密码输入框验证码输入框的输入类型设置为可见,使用户能够看到输入的内容;在密码开关按钮没有被选中时,我们将密码输入框验证码输入框的输入类型设置为不可见,以保护用户的隐私。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值