Edit text for OTP with Each letter in separate positions

后端 未结 19 993
别跟我提以往
别跟我提以往 2020-12-13 00:25

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 wi

相关标签:
19条回答
  • 2020-12-13 00:56

    Solution with Insertion and deletion support (ViewBinding)

    private class GenericTextWatcher implements TextWatcher {
        private EditText currentView;
        private EditText nextView;
    
        private GenericTextWatcher(EditText currentView, EditText nextView) {
            this.currentView = currentView;
            this.nextView = nextView;
        }
    
        @Override
        public void afterTextChanged(Editable editable) {
            // TODO Auto-generated method stub
            String text = editable.toString();
            if (nextView != null && text.length() == 1) {
                nextView.requestFocus();
            }
            if(text.length() >1){
                currentView.setText(String.valueOf(text.charAt(text.length() - 1)));
                currentView.setSelection(1);
            }
        }
    
        @Override
        public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
            // TODO Auto-generated method stub
        }
    
        @Override
        public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
            // TODO Auto-generated method stub
        }
    }
    
    private class GenericKeyEvent implements View.OnKeyListener {
    
        private EditText currentView;
        private EditText previousView;
    
        public GenericKeyEvent(EditText currentView, EditText previousView) {
            this.currentView = currentView;
            this.previousView = previousView;
        }
    
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DEL && currentView.getText().toString().isEmpty()) {
                if (previousView != null) {
                    previousView.requestFocus();
                }
                return true;
            }
            return false;
        }
    }
    

    private void attachTextWatchers() {
            binding.editText1.addTextChangedListener(new GenericTextWatcher(binding.editText1, binding.editText2));
            binding.editText2.addTextChangedListener(new GenericTextWatcher(binding.editText2, binding.editText3));
            binding.editText3.addTextChangedListener(new GenericTextWatcher(binding.editText3, binding.editText4));
            binding.editText4.addTextChangedListener(new GenericTextWatcher(binding.editText4, null));
    
            binding.editText2.setOnKeyListener(new GenericKeyEvent(binding.editText2, binding.editText1));
            binding.editText3.setOnKeyListener(new GenericKeyEvent(binding.editText3, binding.editText2));
            binding.editText4.setOnKeyListener(new GenericKeyEvent(binding.editText4, binding.editText3));
        }
    
    0 讨论(0)
  • 2020-12-13 00:57

    In Kotlin, You may use bellow like.. It is working fine

    editText1.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
            if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
                //Perform Code
    
                if(editText1.hasFocus()){
                    editText1.setText("")
                    editText1.requestFocus()
                    return@OnKeyListener true
                }
                //return@OnKeyListener true
            }
            false
        })
    
        editText2.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
            if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
                //Perform Code
                if(editText2.hasFocus()){
                    //txtOTP_2.requestFocus()
                    editText2.setText("")
                    editText1.requestFocus()
                    return@OnKeyListener true
                }
                //  return@OnKeyListener true
            }
            false
        })
    
        editText3.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
            if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
                //Perform Code
                if(editText3.hasFocus()){
                    //txtOTP_2.requestFocus()
                    editText3.setText("")
                    editText2.requestFocus()
                    return@OnKeyListener true
                }
                //return@OnKeyListener true
            }
            false
        })
    
        editText4.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
            if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
                //Perform Code
                if(editText4.hasFocus()){
                    // txtOTP_4.requestFocus()
                    editText4.setText("")
                    editText3.requestFocus()
                    return@OnKeyListener true
                }
                // return@OnKeyListener true
            }
            false
        })
    
    0 讨论(0)
  • 2020-12-13 00:58

    SOLUTION 1

    You can subclass a TextWatcher and implement your own logic.

    public class OTPTextWatcher implements TextWatcher {
    
        private EditText view;
        private List<EditText> otpDigitViews;
        private OTPCompleteListener otpListener;
        private static int lastOtpLength;
    
        public OTPTextWatcher(EditText otpView, List<EditText> otpDigitViews, OTPCompleteListener 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) {
                otpListener.onOTPFilled(inputValue);
            } 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 no view left to current edit text");
                        }
                    }
                }
    
                if (OTPTextWatcher.lastOtpLength == 4) {
                    otpListener.onOTPIncomplete();
                }
            }
    
            OTPTextWatcher.lastOtpLength = inputValue.length();
        }
    
        public interface OTPCompleteListener {
            void onOTPFilled(String otp);
    
            void onOTPIncomplete();
        }
    }
    

    Implementaion :

    protected void setEventListeners() {
        OTPTextWatcher.OTPCompleteListener otpCompleteListener = new OTPTextWatcher.OTPCompleteListener() {
            @Override
            public void onOTPFilled(String otp) {
                showLoading();
                verifyOTP(otp);
            }
    
            @Override
            public void onOTPIncomplete() {
            }
        };
    
        for (EditText etOTP : otpViewList) {
            etOTP.addTextChangedListener(new OTPTextWatcher(etOTP, otpViewList, otpCompleteListener));
        }
    }
    

    SOLUTION 2

    integrate android-otpview-pinview to your application.

    0 讨论(0)
  • 2020-12-13 01:01

    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
            }
        });
    
    0 讨论(0)
  • 2020-12-13 01:02

    You can try this, by making TextWatcher more Generic, so its easy to use and understand

    Use below class:

    public class GenericTextWatcher implements TextWatcher
        {
            private View view;
            private GenericTextWatcher(View view) 
            {
                this.view = view;
            }
    
            @Override
            public void afterTextChanged(Editable editable) {
                // TODO Auto-generated method stub
                String text = editable.toString();
                switch(view.getId())
                {
    
                case R.id.editText1:
                    if(text.length()==1)
                        et2.requestFocus(); 
                    break;
                case R.id.editText2:
                    if(text.length()==1)
                        et3.requestFocus();
                    else if(text.length()==0)
                        et1.requestFocus();  
                    break;
                case R.id.editText3:
                    if(text.length()==1)
                        et4.requestFocus();
                    else if(text.length()==0)
                        et2.requestFocus();
                    break;
                case R.id.editText4:
                    if(text.length()==0)
                        et3.requestFocus();
                    break;
                }
            }
    
            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
                // TODO Auto-generated method stub
            }
    
            @Override
            public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
                // TODO Auto-generated method stub
            }
        }
    

    How to use above class

    et1.addTextChangedListener(new GenericTextWatcher(et1));
    et2.addTextChangedListener(new GenericTextWatcher(et2));
    et3.addTextChangedListener(new GenericTextWatcher(et3));
    et4.addTextChangedListener(new GenericTextWatcher(et4));
    

    Here et1,et2,et3 and et4 are your EditTexts, I know its bad naming convention as per Java Standard, but you can replace it with yours.

    P.S You can find the xml design for this here GitHub some other, sample design xml for reference

    0 讨论(0)
  • 2020-12-13 01:04

    Use only one EditText with custom background.

    <RelativeLayout
        android:layout_width="280dp"
        android:layout_height="44dp"
        android:layout_gravity="center"
        android:background="#eef">
    
        <ProgressBar
            android:id="@+id/enterProgressBar"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="10dp"
            android:layout_alignBottom="@+id/idEnterCode"
            android:layout_marginStart="5dp"
            android:layout_marginEnd="5dp"
            android:max="60"
            android:progressBackgroundTint="@android:color/transparent"
            android:progressTint="@color/colorPrimaryDark" />
    
        <EditText
            android:id="@+id/idEnterCode"
            android:layout_width="match_parent"
            android:layout_height="44dp"
            android:background="@drawable/ic_otp_block"
            android:backgroundTint="@color/colorAppBackground"
            android:cursorVisible="false"
            android:imeOptions="flagNoFullscreen|actionDone"
            android:inputType="number"
            android:letterSpacing="1.48"
            android:maxLength="6"
            android:text="5623"
            android:padding="5dp"
            android:textIsSelectable="false"
            android:textSize="22dp"
            tools:ignore="SpUsage" />
    </RelativeLayout>
    

    Here ic_otp_block is a vector asset (using SVG) which is a rectangular block with transparent circular holes on it.

    otp_block.png

    Note: This is a blurry png image, use it for testing or convert it to SVG.

    Now in your Java file:

    private ProgressBar enterProgressBar;
    private EditText enterCode;
    ...
    
    enterCode.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 (enterCode.getText().length() == 0) {
                    setProgressAnimate(enterProgressBar, 0);
                } else if (enterCode.getText().length() == 1) {
                    setProgressAnimate(enterProgressBar, 10);
                } else if (enterCode.getText().length() == 2) {
                    setProgressAnimate(enterProgressBar, 20);
                } else if (enterCode.getText().length() == 3) {
                    setProgressAnimate(enterProgressBar, 30);
                } else if (enterCode.getText().length() == 4) {
                    setProgressAnimate(enterProgressBar, 40);
                } else if (enterCode.getText().length() == 5) {
                    setProgressAnimate(enterProgressBar, 50);
                } else if (enterCode.getText().length() == 6) {
                    setProgressAnimate(enterProgressBar, 60);
                    String code = enterCode.getText().toString().trim();
                    verifyVerificationCode(code);
                }
            }
        });
    ...
        private void setProgressAnimate(ProgressBar pb, int progressTo) {
            ObjectAnimator animation = ObjectAnimator.ofInt(pb, "progress",
     pb.getProgress(), progressTo);
            animation.setDuration(200);
            animation.setInterpolator(new DecelerateInterpolator());
            animation.start();
        }
    

    OUTPUT:

    0 讨论(0)
提交回复
热议问题