Implementation of Android Verification Code Input Box!!!

Posted by Yves on Thu, 23 May 2019 01:02:42 +0200

Preface

Verification code input box is an indispensable component of many APP s. In the past, when reconstructing the login page, the UI was redesigned, so it can't be done simply with EditText, so this article will share how to implement a common verification code input box.

text

The key point is in the input box. Maybe most APP s use the UI effect of six boxes. I use six horizontal lines to mark out the position of six numbers according to our design requirements. At first I thought about using six TextViews directly and then passing the focus, but I found it difficult to implement. After checking on the internet, we found that the more reliable way is to use 6 TextViews plus an EditText, which is also implemented in accordance with this method. But later, when testing, we found a problem: the way given on the Internet needs to monitor the deletion button of the soft keyboard.

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

This is a well-known way of writing, but this method of listening is not reliable (it can not be heard on Android's native keyboard), because whether the monitoring triggers or not, there is no mandatory requirement, depending on the mood of the input method developer, which is described in the official document:

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

Only input, not delete, this is not possible ah, users will certainly scold Niang, I do not want to be taken to sacrifice the heavens or anything.
So we can only try to make some modifications on the original basis to avoid this problem. The final solution is to use an array of TextView to maintain six TextViews, then hide a transparent EditTextView to receive the user's input content, and then display the input content on six TextViews. The UI can be designed at will. In the process of implementation, one of the key problems encountered is: when the input content exceeds 6 bits, how should I deal with it? The first plan is to judge the number of digits currently input and then do the corresponding processing. The online scheme is also so realized. I think later, it doesn't need so much trouble at all. It only needs one line of attributes to solve this problem.

android:maxLength="6"

Simply limit its maximum length in the attributes of EditText, instead of processing in the code, simply copy the contents of EditTextView to TextView completely.
The final complete code is as follows:

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);//hide cursor
        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;
    }

}

After the reminder of thisfeng, several problems were found:

1. Double-click and long-click will select the content of EditText, and there will be options such as copy and paste.

2. The cursor position changes with the click, and the input number may be inserted into the middle position.

Corresponding modifications were made:

//Shielding Long Press Event
android:longClickable="false"

//Use custom 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);
        //Fix the cursor position at the end
        this.setSelection(this.getText().length());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //Shield Double-click Events
        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);
    }
}

epilogue

Sometimes we need to consider not only the final effect, but also the cost of time. It is of course the best way to achieve it in the simplest way. It is also good to save time to play Kunte. And the less code you write, the less likely you will make mistakes, right? Share it here today. I'm going to cook my Similu. If there are any mistakes, you are welcome to correct them.

Topics: Android less