美文网首页Andorid的好东西干货区Android
Android 验证码输入框的实现

Android 验证码输入框的实现

作者: 实例波 | 来源:发表于2017-09-10 15:47 被阅读837次

    前言

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

    正文

    先搂一眼效果吧


    选择原谅的验证码输入框.gif

    不要把注意力都放在头顶的那一抹绿上,重点在输入框,可能大多数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;
        }
    
    }
    

    2017.10.4-Bugfix

    经过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);
        }
    }
    
    

    如果需要完整的demo,可以访问我的github:https://github.com/jb274585381/VerifyCodeViewDemo

    结语

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

    相关文章

      网友评论

      • Chen_ba7f:我遇到5楼一样的问题了,无法拿到点击事件,点击也无法得到焦点
        实例波:@Chen_ba7f 你把我的demo拉下来跑一下试试,如果demo没问题,你再对比一下你自己的代码
        Chen_ba7f:@实例波 布局就是一个线性布局,很简单的布局.
        我给verifyCodeView.requestFocus();强制获取焦点也没用 就是弹不出键盘,点了没用.
        实例波:能详细描述一下吗? 是用我的demo有问题 还是在你自己项目里出现了问题,你的布局文件咋写的?
      • ChangQin:回调不执行?
      • b27343c41842:先谢过作者,目前无法拿到点击事件,点击也无法得到焦点(当前存在两个edittext),这个如何解决
        Chen_ba7f:我也遇到这到个问题了,你是怎么解决的了?
      • 黑白咖:签到
      • thisfeng:拿来用了,提两个问题, 需要 禁用长按事件 和 双击事件。因为会选中EditText 的焦点,提示你复制还是粘贴。解决这两个问题。 android:longClickable="false" EditText长按事件设为 false 以及android:layout_width="0dp" 宽度设置为0. 默认进入打开键盘打开键盘。 关闭键盘时 不能打开,这时加个监听获取EditText 设置 打开键盘即可
        实例波:确实存在你提出的问题,已经做了相应的修改,非常感谢~
      • yunhom:作者的思路很好,简单又实用:+1:

      本文标题:Android 验证码输入框的实现

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