0

Here is 4 EditText for input a numeric password. I want it to be like, If first EditText is filled by 1 number then, focus should goes to next EditText and should also work same in reverse manner. So that user can keep entering password from Left most and can also erase same way from Right most.

Can someone suggest what can be the best approach to proceed?

Currently looks like this

Milind Mevada
  • 3,145
  • 1
  • 14
  • 22
Sudip
  • 647
  • 2
  • 7
  • 28

6 Answers6

5

You cannot complete it using addTextChangedListener alone. You may have to set onKeyListener along with it. Here is the code for you:

//6 EditTexts are otpEt[0], otpEt[1],...otpEt[5]
private EditText[] otpEt = new EditText[6];
    otpEt[0] = (EditText) findViewById(R.id.otpEt1);
    otpEt[1] = (EditText) findViewById(R.id.otpEt2);
    otpEt[2] = (EditText) findViewById(R.id.otpEt3);
    otpEt[3] = (EditText) findViewById(R.id.otpEt4);
    otpEt[4] = (EditText) findViewById(R.id.otpEt5);
    otpEt[5] = (EditText) findViewById(R.id.otpEt6);

setOtpEditTextHandler();

private void setOtpEditTextHandler () { //This is the function to be called
    for (int i = 0;i < 6;i++) { //Its designed for 6 digit OTP
        final int iVal = i;

        otpEt[iVal].addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

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

            }

            @Override
            public void afterTextChanged(Editable s) {
                if(iVal == 5 && !otpEt[iVal].getText().toString().isEmpty()) {
                    otpEt[iVal].clearFocus(); //Clears focus when you have entered the last digit of the OTP.
                } else if (!otpEt[iVal].getText().toString().isEmpty()) {
                    otpEt[iVal+1].requestFocus(); //focuses on the next edittext after a digit is entered.
                }
            }
        });

        otpEt[iVal].setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() != KeyEvent.ACTION_DOWN) {
                    return false; //Dont get confused by this, it is because onKeyListener is called twice and this condition is to avoid it.
                }
                if(keyCode == KeyEvent.KEYCODE_DEL && 
otpEt[iVal].getText().toString().isEmpty() && iVal != 0) {
//this condition is to handel the delete input by users.
                    otpEt[iVal-1].setText("");//Deletes the digit of OTP
                    otpEt[iVal-1].requestFocus();//and sets the focus on previous digit
                }
                return false;
            }
        });
    }
}

If you feel this code difficult, just paste it into your project and try rearranging things. You will be able to get it easily

Bensal
  • 3,316
  • 1
  • 23
  • 35
  • 1
    Instead of using static number 6 and 5 in for loop and if condition we can use `otpEt.length` and `(otpEt .lengt - 1)`, It'll work for any no of edtitexts dynamically. – Thamilan S Aug 26 '21 at 06:08
3

If you are familiar with RxJava, then this might be easiest way to fulfill your need. Here is a sample of Kotlin code

RxTextView.textChanges(edtOtp1).filter { it.length == 1 }.subscribe { edtOtp2.requestFocus() }
RxTextView.textChanges(edtOtp2).filter { it.length == 1 }.subscribe { edtOtp3.requestFocus() }
RxTextView.textChanges(edtOtp3).filter { it.length == 1 }.subscribe { edtOtp4.requestFocus() }
RxTextView.textChanges(edtOtp4).filter { it.length == 1 }.subscribe { context.hideKeyboard(view) }

The same way you can write for reverse also. While length is zero focus to the previous Edittext.

Milind Mevada
  • 3,145
  • 1
  • 14
  • 22
2

You can use the library Android PinView / OtpView

or you can use addTextChangedListener to add a TextWatcher which is called whenever this EditTextView's text changes then you can call View.requestFocus() on the next EditText to focus it

Arpan Sarkar
  • 2,301
  • 2
  • 13
  • 24
2

You can do this

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    >

    <EditText
        android:id="@+id/otpET1"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:inputType="number"
        android:maxLength="1"
        android:gravity="center"
        android:textSize="20sp"/>

    <EditText
        android:id="@+id/otpET2"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:inputType="number"
        android:maxLength="1"
        android:gravity="center"
        android:textSize="20sp"/>
    <EditText
        android:id="@+id/otpET3"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:inputType="number"
        android:maxLength="1"
        android:gravity="center"
        android:textSize="20sp"/>

    <EditText
        android:id="@+id/otpET4"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:inputType="number"
        android:maxLength="1"
        android:gravity="center"
        android:textSize="20sp"/>
    <EditText
        android:id="@+id/otpET5"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:inputType="number"
        android:maxLength="1"
        android:gravity="center"
        android:textSize="20sp"/>
    <EditText
        android:id="@+id/otpET6"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:inputType="number"
        android:gravity="center"
        android:maxLength="1"
        android:textSize="20sp"/>
</LinearLayout>

In activity

EditText[] otpETs = new EditText[6];


otpETs[0] = findViewById(R.id.otpET1);
otpETs[1] = findViewById(R.id.otpET2);
otpETs[2] = findViewById(R.id.otpET3);
otpETs[3] = findViewById(R.id.otpET4);
otpETs[4] = findViewById(R.id.otpET5);
otpETs[5] = findViewById(R.id.otpET6);

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    int keyCode = event.getKeyCode();
    if (keyCode == 7 || keyCode == 8 ||
            keyCode == 9 || keyCode == 10 ||
            keyCode == 11 || keyCode == 12 ||
            keyCode == 13 || keyCode == 14 ||
            keyCode == 15 || keyCode == 16 ||
            keyCode == 67) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            if (keyCode == KEYCODE_DEL) {
                int index = checkWhoHasFocus();
                if (index != 123) {
                    if (Helpers.rS(otpETs[index]).equals("")) {
                        if (index != 0) {
                            otpETs[index - 1].requestFocus();
                        }
                    } else {
                        return super.dispatchKeyEvent(event);
                    }
                }
            } else {
                int index = checkWhoHasFocus();
                if (index != 123) {
                    if (Helpers.rS(otpETs[index]).equals("")) {
                        return super.dispatchKeyEvent(event);
                    } else {
                        if (index != 5) {
                            otpETs[index + 1].requestFocus();
                        }
                    }
                }
                return super.dispatchKeyEvent(event);
            }
        }
    } else {
        return super.dispatchKeyEvent(event);
    }
    return true;
}

private int checkWhoHasFocus() {
    for (int i = 0; i < otpETs.length; i++) {
        EditText tempET = otpETs[i];
        if (tempET.hasFocus()) {
            return i;
        }
    }
    return 123;
}

This is just to get string from editTexts

public class Helpers {

    public static String rS(EditText editText) {
        return editText.getText().toString().trim();
    }
}

Finally,

String code = Helpers.rS(otpETs[0]) + Helpers.rS(otpETs[1]) + 
Helpers.rS(otpETs[2]) + Helpers.rS(otpETs[3]) + Helpers.rS(otpETs[4]) 
+ Helpers.rS(otpETs[5]);

or just use a simple for/while loop.

Kaishu Sahu
  • 129
  • 7
  • how to implement this in a fragment in activity it is working as expected but not in fragment – tintin Jul 10 '22 at 11:59
1

In Kotlin You may use bellow like ..

txtOTP_1.addTextChangedListener(object : TextWatcher {
     override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
         if (txtOTP_1.text.toString().length == 1) {
             txtOTP_1.clearFocus()
             txtOTP_2.requestFocus()
             txtOTP_2.setCursorVisible(true)
         }
     }

     override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {

     }

     override fun afterTextChanged(s: Editable) {
         if (txtOTP_1.text.toString().length == 0) {
             txtOTP_1.requestFocus()
         }
     }
 })


 txtOTP_2.addTextChangedListener(object : TextWatcher {
     override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
         if (txtOTP_2.text.toString().length == 1) {           
             txtOTP_2.clearFocus()
             txtOTP_3.requestFocus()
             txtOTP_3.setCursorVisible(true)

         }
     }

     override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
     }

     override fun afterTextChanged(s: Editable) {
         if (txtOTP_2.text.toString().length == 0) {               
             txtOTP_2.requestFocus()
         }

     }
 })

 txtOTP_3.addTextChangedListener(object : TextWatcher {
     override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
         if (txtOTP_3.text.toString().length == 1) {                
             txtOTP_3.clearFocus()
             txtOTP_4.requestFocus()
             txtOTP_4.setCursorVisible(true)               
         }
     }

     override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
     }

     override fun afterTextChanged(s: Editable) {
         if (txtOTP_3.text.toString().length == 0) {             
             txtOTP_3.requestFocus()
         }

     }
 })

 txtOTP_4.addTextChangedListener(object : TextWatcher {
     override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
         if (txtOTP_4.text.toString().length == 1) {                
             txtOTP_4.clearFocus()
             txtOTP_5.requestFocus()
             txtOTP_5.setCursorVisible(true)
         }
     }

     override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
     }

     override fun afterTextChanged(s: Editable) {
         if (txtOTP_4.text.toString().length == 0) {
             txtOTP_4.requestFocus()
         }           
     }
 })


 txtOTP_5.addTextChangedListener(object : TextWatcher {
     override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
         if (txtOTP_5.text.toString().length == 1) {              
             txtOTP_5.requestFocus()
             txtOTP_5.setCursorVisible(true)

         }
     }
  })
Enamul Haque
  • 4,789
  • 1
  • 37
  • 50
0

the first you make halper

class GenericTextWatcher(private val currentView: EditText,nextView: EditText?) :
TextWatcher {
private val nextView: EditText?
override fun afterTextChanged(editable: Editable) {
    // TODO Auto-generated method stub
    val text = editable.toString()
    if (nextView != null && text.length == 1) {
        nextView.requestFocus()
    }
    if (text.length > 1) {
        currentView.setText(text[text.length - 1].toString())
        currentView.setSelection(1)
    }
}

override fun beforeTextChanged(arg0: CharSequence, arg1: Int, arg2: Int, arg3: Int) {
    // TODO Auto-generated method stub
}

override fun onTextChanged(arg0: CharSequence, arg1: Int, arg2: Int, arg3: Int) {
    // TODO Auto-generated method stub
}

init {
    this.nextView = nextView
}


class GenericKeyEvent(
private val currentView: EditText,
private val previousView: EditText?
                         ) :
View.OnKeyListener {
override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
    if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DEL && currentView.text.toString()
            .isEmpty()
    ) {
        previousView?.requestFocus()
        return true
    }
    return false
}

and you can use this function

 private fun attachTextWatchersSMS(){
    otpactivityBinding.etKode1.addTextChangedListener(GenericTextWatcher(otpactivityBinding.etKode1, otpactivityBinding.etKode2))
    otpactivityBinding.etKode2.addTextChangedListener(GenericTextWatcher(otpactivityBinding.etKode2, otpactivityBinding.etKode3))
    otpactivityBinding.etKode3.addTextChangedListener(GenericTextWatcher(otpactivityBinding.etKode3, otpactivityBinding.etKode4))
    otpactivityBinding.etKode4.addTextChangedListener(GenericTextWatcher(otpactivityBinding.etKode4, otpactivityBinding.etKode5))
    otpactivityBinding.etKode5.addTextChangedListener(GenericTextWatcher(otpactivityBinding.etKode5, otpactivityBinding.etKode6))
    otpactivityBinding.etKode6.addTextChangedListener(GenericTextWatcher(otpactivityBinding.etKode6, otpactivityBinding.etKode7))
    otpactivityBinding.etKode7.addTextChangedListener(GenericTextWatcher(otpactivityBinding.etKode7, otpactivityBinding.etKode8))
    otpactivityBinding.etKode8.addTextChangedListener(GenericTextWatcher(otpactivityBinding.etKode8, null))

    otpactivityBinding.etKode2.setOnKeyListener(GenericKeyEvent(otpactivityBinding.etKode2, otpactivityBinding.etKode1))
    otpactivityBinding.etKode3.setOnKeyListener(GenericKeyEvent(otpactivityBinding.etKode3, otpactivityBinding.etKode2))
    otpactivityBinding.etKode4.setOnKeyListener(GenericKeyEvent(otpactivityBinding.etKode4, otpactivityBinding.etKode3))
    otpactivityBinding.etKode5.setOnKeyListener(GenericKeyEvent(otpactivityBinding.etKode5, otpactivityBinding.etKode4))
    otpactivityBinding.etKode6.setOnKeyListener(GenericKeyEvent(otpactivityBinding.etKode6, otpactivityBinding.etKode5))
    otpactivityBinding.etKode7.setOnKeyListener(GenericKeyEvent(otpactivityBinding.etKode7, otpactivityBinding.etKode6))
    otpactivityBinding.etKode8.setOnKeyListener(GenericKeyEvent(otpactivityBinding.etKode8, otpactivityBinding.etKode7))

}