使用 EditText 连续输入 OTP

Continuous OTP input with EditText

这里是4EditText输入数字密码。我希望它是这样的,如果第一个 EditText 被 1 个数字填充,那么焦点应该转到下一个 EditText 并且也应该以相反的方式工作。这样用户可以一直从最左边输入密码,也可以从最右边用同样的方式擦除。

有人可以建议最好的方法是什么吗?

如果您熟悉 RxJava,那么这可能是满足您需求的最简单方法。 这是 Kotlin 代码示例

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

你也可以用同样的方式写反向。当长度为零时,焦点指向前一个 Edittext。

您可以使用图书馆 Android PinView / OtpView

或者您可以使用 addTextChangedListener 添加一个 TextWatcher ,每当此 EditTextView 的文本更改时调用它,然后您可以在下一个 EditText 上调用 View.requestFocus() 来聚焦它

你可以做到

<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>

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

这只是为了从 editTexts 中获取字符串

public class Helpers {

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

最后,

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

或者只使用一个简单的 for/while 循环。

在 Kotlin 中你可以像 ..

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)

         }
     }
  })

您无法单独使用 addTextChangedListener 完成它。您可能必须同时设置 onKeyListener。这是给你的代码:

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

如果您觉得这段代码很难,只需将其粘贴到您的项目中并尝试重新排列。您将能够轻松获得它

你做的第一件事

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
}

你可以使用这个功能

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

}