Edit text for OTP with Each letter in separate pos

2019-03-15 21:45发布

Hi I'm working on a application which asks for OTP when user want to reset his password for which I need a text like the one in attached Image... What I thought to proceed with is individual text for each of the letter, All of them arranged in linear layout of horizontal orientation with some margin... and max length as 1 so only one letter can be entered in each editText... Is that a right Approach?? Any Suggestions??

enter image description here

10条回答
forever°为你锁心
2楼-- · 2019-03-15 21:47

using DataBinding layout:

public class EnterOTPActivity extends AppCompatActivity {
            private ActivityEnterOtpBinding binding;
            private Context mContext;
            private int currentEditIndex;


            @Override
            protected void onCreate(@Nullable Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                binding = DataBindingUtil.setContentView(this, R.layout.activity_enter_otp);
                mContext = this;

                binding.et1.addTextChangedListener(new MyTextChangeWatcher(1));
                binding.et2.addTextChangedListener(new MyTextChangeWatcher(2));
                binding.et3.addTextChangedListener(new MyTextChangeWatcher(3));
                binding.et4.addTextChangedListener(new MyTextChangeWatcher(4));
                binding.et5.addTextChangedListener(new MyTextChangeWatcher(5));
                binding.et6.addTextChangedListener(new MyTextChangeWatcher(6));

                binding.et1.setOnKeyListener(keyListener);
                binding.et2.setOnKeyListener(keyListener);
                binding.et3.setOnKeyListener(keyListener);
                binding.et4.setOnKeyListener(keyListener);
                binding.et5.setOnKeyListener(keyListener);
                binding.et6.setOnKeyListener(keyListener);


            }

            private View.OnKeyListener keyListener = new View.OnKeyListener() {

                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {

                    if ((((EditText) v).getText().toString() == null || ((EditText) v)
                            .getText().toString().isEmpty())
                            && keyCode == KeyEvent.KEYCODE_DEL
                            && event.getAction() == KeyEvent.ACTION_DOWN) {

                        if (currentEditIndex == 6)
                            currentEditIndex = 5;
                        if (currentEditIndex > 0) {
                            EditText editText = getEditTextFromIndex(currentEditIndex);
                           editText.setText("");
                            editText.requestFocusFromTouch();
                            currentEditIndex--;
                        }
                    }

                    return false;
                }
            };

            class MyTextChangeWatcher implements TextWatcher {
                private int index;

                public MyTextChangeWatcher(int index) {
                    super();
                    this.index = index;
                }

                @Override
                public void afterTextChanged(Editable s) {
                    if (s != null && s.length() == 1) {
                        if (index < 7) {

                            if (index < 6) {
                                EditText editText = getEditTextFromIndex(index);
                                editText.clearFocus();
                                getEditTextFromIndex(index + 1).requestFocusFromTouch();
                            }
                            currentEditIndex = index;
                        } else {

                        }
                    } else {
                    }
                }

                @Override
                public void beforeTextChanged(CharSequence s, int start, int count,
                                              int after) {
                }

                @Override
                public void onTextChanged(CharSequence s, int start, int before,
                                          int count) {
                }
            }

            private EditText getEditTextFromIndex(int index) {
                switch (index) {
                    case 1:
                        return binding.et1;
                    case 2:
                        return binding.et2;
                    case 3:
                        return binding.et3;
                    case 4:
                        return binding.et4;
                    case 5:
                        return binding.et5;
                    case 6:
                        return binding.et6;

                    default:
                        break;
                }
                return null;
            }
        }
查看更多
霸刀☆藐视天下
3楼-- · 2019-03-15 21:47

These are some of the links that might help. You can customize it as per your requirement.

This link is a step by step tutorial on how you can create pin entry UI for yourself. https://medium.com/@ali.muzaffar/building-a-pinentryedittext-in-android-5f2eddcae5d3#.ldp03yky7

PinEntry android Library https://github.com/Philio/PinEntryView

查看更多
放荡不羁爱自由
4楼-- · 2019-03-15 21:51

You can subclass a TextWatcher and implement your own logic.

public class OTPTextWatcher implements TextWatcher {

private EditText view;
private List<EditText> otpDigitViews;
private OTPListener otpListener;

public OTPTextWatcher(EditText otpView, List<EditText> otpDigitViews, OTPListener listener) {
    view = otpView;
    this.otpDigitViews = otpDigitViews;
    this.otpListener = listener;
}


@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) {

    String digit1 = otpDigitViews.get(0).getText().toString();
    String digit2 = otpDigitViews.get(1).getText().toString();
    String digit3 = otpDigitViews.get(2).getText().toString();
    String digit4 = otpDigitViews.get(3).getText().toString();
    String currentDigit = editable.toString();
    final String inputValue = digit1 + digit2 + digit3 + digit4;

    if (inputValue.length() == 4) {
        if (inputValue.equals("1234")) {
            otpListener.onOTPSuccess();
        } else {
            otpListener.onOTPError();
        }
    } else {
        if (currentDigit.length() >= 1
                && view != otpDigitViews.get(3)) {
            if (view != null)
                view.focusSearch(View.FOCUS_RIGHT).requestFocus();
        } else {
            if (currentDigit.length() <= 0 && view.getSelectionStart() <= 0) {
                try {
                    view.focusSearch(View.FOCUS_LEFT).requestFocus();
                } catch (NullPointerException e) {
                    LogHelper.printErrorLog("There is not view left to current edit text");
                }
            }
        }
    }
}

public interface OTPListener {
    void onOTPSuccess();

    void onOTPError();
}}
查看更多
不美不萌又怎样
5楼-- · 2019-03-15 21:55

The below solution takes into consideration:

  1. Auto focusing to the next edit text on entering one digit of OTP in the focussed edit text.

  2. Auto focusing to the previous edit text on deleting one digit of OTP in the focussed edit text.

The combination of work in onTextChanged() and afterTextChanged() helps in achieving the same.

private EditText firstDigitOtpEdt, secondDigitOtpEdt, thirdDigitOtpEdt, fourthDigitOtpEdt;

firstDigitOtpEdt.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) {
            if (firstDigitOtpEdt.getText().toString().length() == 1) {
                secondDigitOtpEdt.requestFocus();
            }
        }
    });

secondDigitOtpEdt.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) {
            if (secondDigitOtpEdt.getText().toString().length() == 0) {
                firstDigitOtpEdt.requestFocus();
            }
        }

        @Override
        public void afterTextChanged(Editable editable) {
            if (secondDigitOtpEdt.getText().toString().length() == 1) {
                thirdDigitOtpEdt.requestFocus();
            }
        }
    });

thirdDigitOtpEdt.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) {
            if (thirdDigitOtpEdt.getText().toString().length() == 0) {
                secondDigitOtpEdt.requestFocus();
            }
        }

        @Override
        public void afterTextChanged(Editable editable) {
            if (thirdDigitOtpEdt.getText().toString().length() == 1) {
                fourthDigitOtpEdt.requestFocus();
            }
        }
    });

fourthDigitOtpEdt.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) {
            if (fourthDigitOtpEdt.getText().toString().length() == 0) {
                thirdDigitOtpEdt.requestFocus();
            }
        }

        @Override
        public void afterTextChanged(Editable editable) {
           // We can call api to verify the OTP here or on an explicit button click
        }
    });
查看更多
贪生不怕死
6楼-- · 2019-03-15 21:56

enter image description here

I implemented the following code based on other answers.

I wanted this code to be very simple, optimized and understandable for changes.

Don't use android:maxLength="1" in your xml.

//package your package

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class PinActivity extends AppCompatActivity {

    private EditText editText1, editText2, editText3, editText4;
    private EditText[] editTexts;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pin);

        editText1 = (EditText) findViewById(R.id.otpEdit1);
        editText2 = (EditText) findViewById(R.id.otpEdit2);
        editText3 = (EditText) findViewById(R.id.otpEdit3);
        editText4 = (EditText) findViewById(R.id.otpEdit4);
        editTexts = new EditText[]{editText1, editText2, editText3, editText4};

        editText1.addTextChangedListener(new PinTextWatcher(0));
        editText2.addTextChangedListener(new PinTextWatcher(1));
        editText3.addTextChangedListener(new PinTextWatcher(2));
        editText4.addTextChangedListener(new PinTextWatcher(3));

        editText1.setOnKeyListener(new PinOnKeyListener(0));
        editText2.setOnKeyListener(new PinOnKeyListener(1));
        editText3.setOnKeyListener(new PinOnKeyListener(2));
        editText4.setOnKeyListener(new PinOnKeyListener(3));
    }


    public class PinTextWatcher implements TextWatcher {

        private int currentIndex;
        private boolean isFirst = false, isLast = false;
        private String newTypedString = "";

        PinTextWatcher(int currentIndex) {
            this.currentIndex = currentIndex;

            if (currentIndex == 0)
                this.isFirst = true;
            else if (currentIndex == editTexts.length - 1)
                this.isLast = true;
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            newTypedString = s.subSequence(start, start + count).toString().trim();
        }

        @Override
        public void afterTextChanged(Editable s) {

            String text = newTypedString;

            /* Detect paste event and set first char */
            if (text.length() > 1)
                text = String.valueOf(text.charAt(0)); // TODO: We can fill out other EditTexts

            editTexts[currentIndex].removeTextChangedListener(this);
            editTexts[currentIndex].setText(text);
            editTexts[currentIndex].setSelection(text.length());
            editTexts[currentIndex].addTextChangedListener(this);

            if (text.length() == 1)
                moveToNext();
            else if (text.length() == 0)
                moveToPrevious();
        }

        private void moveToNext() {
            if (!isLast)
                editTexts[currentIndex + 1].requestFocus();

            if (isAllEditTextsFilled() && isLast) { // isLast is optional
                editTexts[currentIndex].clearFocus();
                hideKeyboard();
            }
        }

        private void moveToPrevious() {
            if (!isFirst)
                editTexts[currentIndex - 1].requestFocus();
        }

        private boolean isAllEditTextsFilled() {
            for (EditText editText : editTexts)
                if (editText.getText().toString().trim().length() == 0)
                    return false;
            return true;
        }

        private void hideKeyboard() {
            if (getCurrentFocus() != null) {
                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            }
        }

    }

    public class PinOnKeyListener implements View.OnKeyListener {

        private int currentIndex;

        PinOnKeyListener(int currentIndex) {
            this.currentIndex = currentIndex;
        }

        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
                if (editTexts[currentIndex].getText().toString().isEmpty() && currentIndex != 0)
                    editTexts[currentIndex - 1].requestFocus();
            }
            return false;
        }

    }

}
查看更多
一夜七次
7楼-- · 2019-03-15 21:57

i made a generic TextWatcher for 6 digit OTP:

public class GenericTextWatcher implements TextWatcher {
        private View view;

        GenericTextWatcher(View view) {
            this.view = view;
        }

        @Override
        public void afterTextChanged(Editable s) {
            boolean allOtherFilled = false;
            EditText nextEdit = null;
            EditText previousEdit = null;
            switch (view.getId()) {
                case R.id.otp_et1:
                    allOtherFilled = otpEdit2.getText().length() == 1
                            && otpEdit3.getText().length() == 1
                            && otpEdit4.getText().length() == 1
                            && otpEdit5.getText().length() == 1
                            && otpEdit6.getText().length() == 1;
                    nextEdit = otpEdit2;
                    break;
                case R.id.otp_et2:
                    allOtherFilled = otpEdit1.getText().length() == 1
                            && otpEdit3.getText().length() == 1
                            && otpEdit4.getText().length() == 1
                            && otpEdit5.getText().length() == 1
                            && otpEdit6.getText().length() == 1;
                    nextEdit = otpEdit3;
                    previousEdit = otpEdit1;
                    break;
                case R.id.otp_et3:
                    allOtherFilled = otpEdit1.getText().length() == 1
                            && otpEdit2.getText().length() == 1
                            && otpEdit4.getText().length() == 1
                            && otpEdit5.getText().length() == 1
                            && otpEdit6.getText().length() == 1;
                    nextEdit = otpEdit4;
                    previousEdit = otpEdit2;
                    break;
                case R.id.otp_et4:
                    allOtherFilled = otpEdit1.getText().length() == 1
                            && otpEdit2.getText().length() == 1
                            && otpEdit3.getText().length() == 1
                            && otpEdit5.getText().length() == 1
                            && otpEdit6.getText().length() == 1;
                    nextEdit = otpEdit5;
                    previousEdit = otpEdit3;
                    break;
                case R.id.otp_et5:
                    allOtherFilled = otpEdit1.getText().length() == 1
                            && otpEdit2.getText().length() == 1
                            && otpEdit3.getText().length() == 1
                            && otpEdit4.getText().length() == 1
                            && otpEdit6.getText().length() == 1;
                    nextEdit = otpEdit6;
                    previousEdit = otpEdit4;
                    break;
                case R.id.otp_et6:
                    allOtherFilled = otpEdit1.getText().length() == 1
                            && otpEdit2.getText().length() == 1
                            && otpEdit3.getText().length() == 1
                            && otpEdit4.getText().length() == 1
                            && otpEdit5.getText().length() == 1;
                    previousEdit = otpEdit5;
                    break;
            }

            if (s.length() == 1) {
                if (allOtherFilled) {
                    //if next 2 edit texts are filled , enable the pay button
                    enableDisableButton(continueButton, true);
                    KeyboardUtils.hideKeyboard(LoginActivity.this, (EditText) view);
                }
            } else if (s.length() > 1) {
                if (allOtherFilled) {
                    //if all next edit texts are filled , enable the pay button
                    enableDisableButton(continueButton, true);
                    KeyboardUtils.hideKeyboard(LoginActivity.this, (EditText) view);

                } else if (nextEdit != null) {
                    if (nextEdit.getText().length() == 0) {
                        //if next edit is not filled, move to next edit and set the second digit
                        moveToNextEdit(nextEdit, (EditText) view);
                    } else {
                        //if any other edit is not filled, stay in current edit
                        enableDisableButton(continueButton, false);
                        stayOnCurrentEdit((EditText) view);
                    }
                }
            } else if (s.length() < 1) {
                if (null != previousEdit)
                    moveToPreviousEdit(previousEdit);
                enableDisableButton(continueButton, false);
            }
        }

        @Override
        public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        }

        @Override
        public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
        }

        private void stayOnCurrentEdit(EditText editText) {
            editText.setText(editText.getText().toString().substring(0, 1));
            editText.setSelection(editText.getText().length());
        }

        private void moveToPreviousEdit(EditText editText) {
            editText.setSelection(editText.getText().length());
            editText.requestFocus();
        }

        private void moveToNextEdit(EditText editText2, EditText editText1) {
            editText2.setText(editText1.getText().toString().substring(1, 2));
            editText2.requestFocus();
            editText2.setSelection(editText2.getText().length());
            editText1.setText(editText1.getText().toString().substring(0, 1));
        }
    }

You can add this textWatcher to all your edit texts like this :

this.otpEdit1.addTextChangedListener(new GenericTextWatcher(otpEdit1));
查看更多
登录 后发表回答