使用 EditText 连续 OTP 输入

Posted

技术标签:

【中文标题】使用 EditText 连续 OTP 输入【英文标题】:Continuous OTP input with EditText 【发布时间】:2018-07-23 01:40:42 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

您无法单独使用 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;
            
        );
    

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

【讨论】:

而不是在 for 循环中使用静态数字 6 和 5,如果条件我们可以使用 otpEt.length(otpEt .lengt - 1),它将适用于任何动态的 edtitexts。【参考方案2】:

如果您熟悉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 的关注。

【讨论】:

【参考方案3】:

您可以使用库android PinView / OtpView

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

【讨论】:

【参考方案4】:

你可以这样做

<LinearLayout
    android:layout_
    android:layout_
    android:orientation="horizontal"
    >

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

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

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

活动中

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 循环。

【讨论】:

【参考方案5】:

在 Kotlin 中,您可以像 .. 一样使用 bellow

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)

         
     
  )

【讨论】:

以上是关于使用 EditText 连续 OTP 输入的主要内容,如果未能解决你的问题,请参考以下文章

16-输入框,文本编辑框EditText

android如何改变editText控件中部分文字的格式

我有个可以写入多行文本的EditText,现在想固定每行可输入的字符数,如何使用addTextChangedListener控制

使用edit-text中提供的值设置日历类实例

EditText输入手机号自动带空格

android studio edit text 输入完成后键盘如何退出