编辑OTP文本,每个字母位于不同的位置

时间:2016-08-10 11:45:04

标签: android android-edittext

嗨,我正在开发一个应用程序,当用户想要重置他的密码时我要求OTP,我需要一个像附加图像中那样的文本...我想要继续的是每个人的单独文本字母,所有这些都以水平方向的线性布局排列,有一些边距......最大长度为1,因此每个editText只能输入一个字母......这是正确的方法?? 任何建议??

enter image description here

21 个答案:

答案 0 :(得分:15)

您可以尝试使用 TextWatcher 更多Generic,因此易于使用和理解

使用以下课程:

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

如何使用上述课程

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

这里 et1,et2,et3和et4 是你的EditTexts,我知道它根据Java标准的错误命名约定,但你可以用你的替换它。

P.S 您可以在此处找到xml设计 GitHub some other, sample design xml for reference

答案 1 :(得分:14)

在所有这些答案之后,考虑到UI / UX,我没有找到想要的东西,元素的删除存在缺陷,以至于可以返回到先前的EditText,当前的EditText不应是空的。

这是我在Kotlin中实现的解决方案,该解决方案可通过按键盘上的Delete键进行删除。此外,删除功能的实现方式是,当当前EditText为空并按下Delete键时,它将切换回前一个EditText并删除其元素。

GIF showing the implementation

  1. 这样调用函数:

    //GenericTextWatcher here works only for moving to next EditText when a number is entered
    //first parameter is the current EditText and second parameter is next EditText
    editText1.addTextChangedListener(GenericTextWatcher(editText1, editText2))
    editText2.addTextChangedListener(GenericTextWatcher(editText2, editText3))
    editText3.addTextChangedListener(GenericTextWatcher(editText3, editText4))
    editText4.addTextChangedListener(GenericTextWatcher(editText4, null))
    
    //GenericKeyEvent here works for deleting the element and to switch back to previous EditText
    //first parameter is the current EditText and second parameter is previous EditText
    editText1.setOnKeyListener(GenericKeyEvent(editText1, null))
    editText2.setOnKeyListener(GenericKeyEvent(editText2, editText1))
    editText3.setOnKeyListener(GenericKeyEvent(editText3, editText2))
    editText4.setOnKeyListener(GenericKeyEvent(editText4,editText3))
    
  2. 现在,将这两个类粘贴到当前类中

    class GenericKeyEvent internal constructor(private val currentView: EditText, private val previousView: EditText?) : View.OnKeyListener{
        override fun onKey(p0: View?, keyCode: Int, event: KeyEvent?): Boolean {
            if(event!!.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DEL && currentView.id != R.id.editText1 && currentView.text.isEmpty()) {
                //If current is empty then previous EditText's number will also be deleted
                previousView!!.text = null 
                previousView.requestFocus()
                return true
            }
            return false
        }
    
    
    }
    
    class GenericTextWatcher internal constructor(private val currentView: View, private val nextView: View?) : TextWatcher {
        override fun afterTextChanged(editable: Editable) { // TODO Auto-generated method stub
            val text = editable.toString()
            when (currentView.id) {
                R.id.editText1 -> if (text.length == 1) nextView!!.requestFocus()
                R.id.editText2 -> if (text.length == 1) nextView!!.requestFocus()
                R.id.editText3 -> if (text.length == 1) nextView!!.requestFocus()
                //You can use EditText4 same as above to hide the keyboard
            }
        }
    
        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
        }
    
    }
    

此外,要禁用可见光标,可以在布局的android:cursorVisible="false"标签中使用EditText,也可以使用Java函数setCursorVisible(false)

编辑:我使用的是股票小部件EditTexts,因此,如果您想在它们周围显示一个框,只需创建一个可绘制的版面并将其设置为EditTexts的背景并给它们填充5dp。这将创建一个框,并使它看起来更酷。

答案 2 :(得分:6)

enter image description here

OtpEditText.java(自定义EditText):

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.Editable;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.View;
import androidx.appcompat.widget.AppCompatEditText;

public class OtpEditText extends AppCompatEditText {
    private float mSpace = 24; //24 dp by default, space between the lines
    private float mNumChars = 4;
    private float mLineSpacing = 8; //8dp by default, height of the text from our lines
    private int mMaxLength = 4;
    private float mLineStroke = 2;
    private Paint mLinesPaint;
    private OnClickListener mClickListener;

    public OtpEditText(Context context) {
        super(context);
    }

    public OtpEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public OtpEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        float multi = context.getResources().getDisplayMetrics().density;
        mLineStroke = multi * mLineStroke;
        mLinesPaint = new Paint(getPaint());
        mLinesPaint.setStrokeWidth(mLineStroke);
        mLinesPaint.setColor(getResources().getColor(R.color.colorPrimaryDark));
        setBackgroundResource(0);
        mSpace = multi * mSpace; //convert to pixels for our density
        mLineSpacing = multi * mLineSpacing; //convert to pixels for our density
        mNumChars = mMaxLength;

        super.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // When tapped, move cursor to end of text.
                setSelection(getText().length());
                if (mClickListener != null) {
                    mClickListener.onClick(v);
                }
            }
        });
    }

    @Override
    public void setOnClickListener(OnClickListener l) {
        mClickListener = l;
    }

    @Override
    public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
        throw new RuntimeException("setCustomSelectionActionModeCallback() not supported.");
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int availableWidth = getWidth() - getPaddingRight() - getPaddingLeft();
        float mCharSize;
        if (mSpace < 0) {
            mCharSize = (availableWidth / (mNumChars * 2 - 1));
        } else {
            mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
        }

        int startX = getPaddingLeft();
        int bottom = getHeight() - getPaddingBottom();

        //Text Width
        Editable text = getText();
        int textLength = text.length();
        float[] textWidths = new float[textLength];
        getPaint().getTextWidths(getText(), 0, textLength, textWidths);

        for (int i = 0; i < mNumChars; i++) {
            canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mLinesPaint);
            if (getText().length() > i) {
                float middle = startX + mCharSize / 2;
                canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2, bottom - mLineSpacing, getPaint());
            }
            if (mSpace < 0) {
                startX += mCharSize * 2;
            } else {
                startX += mCharSize + mSpace;
            }
        }
    }
}

在XML中使用此自定义的EditText,如下所示:

<OtpEditText
    android:id="@+id/et_otp"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:cursorVisible="false"
    android:digits="1234567890"
    android:inputType="number"
    android:maxLength="4"
    android:textIsSelectable="false"
    android:textSize="20sp"/>

参考:
文章https://medium.com/@ali.muzaffar/building-a-pinentryedittext-in-android-5f2eddcae5d3
示例代码: https://gist.github.com/alphamu/0d3055e0233c5749b8d6

答案 3 :(得分:5)

enter image description here

我根据其他答案实现了以下代码。

我希望这段代码非常简单,经过优化并且可以理解更改。

请勿在您的xml中使用aws s3 ls

aws cp --recursive s3://<source bucket> s3://<destination bucket>

答案 4 :(得分:2)

使用4种不同的EditText。使用以下代码在后续输入后更改焦点。

private EditText editText1;
private EditText editText2;
private EditText editText3;
private EditText editText4;

    editText1.addTextChangedListener(new TextWatcher() {

            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if (editText1.getText().toString().length() == 1)     //size as per your requirement
                {
                    editText2.requestFocus();
                }
            }

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

            public void afterTextChanged(Editable s) {
            }

        });

    editText2.addTextChangedListener(new TextWatcher() {

            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if (editText2.getText().toString().length() == 1)     //size as per your requirement
                {
                    editText3.requestFocus();
                }
            }

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

            public void afterTextChanged(Editable s) {
            }

        });

依旧......

连接所有EditText中的文本。

答案 5 :(得分:2)

我为6位数的OTP制作了通用的TextWatcher:

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

您可以将此textWatcher添加到所有这样的编辑文本中:

this.otpEdit1.addTextChangedListener(new GenericTextWatcher(otpEdit1));

答案 6 :(得分:1)

使用 Kotlin,您可以使用这种方法:

fun configOtpEditText(vararg etList: EditText) {                          
    val afterTextChanged = { index: Int, e: Editable? ->                  
        val view = etList[index]                                          
        val text = e.toString()                                           
                                                                          
        when (view.id) {                                                  
            // first text changed                                         
            etList[0].id -> {                                             
                if (text.isNotEmpty()) etList[index + 1].requestFocus()   
            }                                                             
                                                                          
            // las text changed                                           
            etList[etList.size - 1].id -> {                               
                if (text.isEmpty()) etList[index - 1].requestFocus()      
            }                                                             
                                                                          
            // middle text changes                                        
            else -> {                                                     
                if (text.isNotEmpty()) etList[index + 1].requestFocus()   
                else etList[index - 1].requestFocus()                     
            }                                                             
        }                                                                 
        false                                                              
    }                                                                     
    etList.forEachIndexed { index, editText ->                            
        editText.doAfterTextChanged { afterTextChanged(index, it) }       
    }                                                                     
}                                                                         

像这样配置尽可能多的 EditText :

configOtpEditText(   
        binding.et1, 
        binding.et2, 
        binding.et3, 
        binding.et4, 
        binding.et5, 
        binding.et6  
)                    

答案 7 :(得分:1)

首先制作一个Text watcher类 以便如果您有多个需要 nextFocus 的屏幕可以通过此完成 所以 TextWatcher 类

公共类 GenericTextWatcher 实现 TextWatcher { 私人最终 EditText etPrev,etNext;

private View view;
public GenericTextWatcher(EditText etNext ,EditText etPrev)
{
    this.etPrev=etPrev;
    this.etNext=etNext;
}

@Override
public void afterTextChanged(Editable editable) {
    String text = editable.toString();
   if(text.length()==1){
       etNext.requestFocus();
   }else{
       etPrev.requestFocus();
   }
}

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

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

}

和 EditText nextFocus 自动化代码

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

答案 8 :(得分:1)

我为此创建了一个简单的库。看看这个。 https://github.com/hexdecimal16/EditTextPin
用法

  1. 布局XML:
<com.dhairytripathi.library.EditTextPin
     android:id="@+id/editTextPin"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content" />
  1. JAVA文件:
EditTextPin editTextPin = findViewById(R.id.editTextPin);
String pin = editTextPin.getPin(); //To get the current entered pin
  1. 其他样式:
app:underlineColor="" <!-- To change underline color-->

答案 9 :(得分:1)

在Kotlin中,您可以像下面这样使用波纹管。效果很好

name: NPM Publish
on:
  push:
    branches: 
      - master

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
        with:
          node-version: 12.x

      - name: Get yarn cache directory
        id: yarn-cache-dir-path
        run: echo "::set-output name=dir::$(yarn cache dir)"

      - uses: actions/cache@v1
        id: yarn-cache
        with:
          path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-

      - name: Install dependencies
        if: steps.yarn-cache.outputs.cache-hit != 'true'
        run: yarn install --frozen-lockfile

      - name: Test cases
        run: yarn run pdn:test

答案 10 :(得分:1)

对于@SachinMandhare在https://stackoverflow.com/a/57337907/8765580上提出的问题

只需将代码修改为

String pattern = "";
for (int j = 0; j < getText().toString().length(); j++) {
    pattern += "*"; //Any Character that you want to display 
}
for (int i = 0; i < mNumChars; i++) {
    canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mLinesPaint);
    if (getText().length() > i) {
         float middle = startX + mCharSize / 2;
         canvas.drawText(pattern, i, i + 1, middle - textWidths[0] / 2, bottom - mLineSpacing, getPaint());
    }
    if (mSpace < 0) {
        startX += mCharSize * 2;
    } else {
        startX += mCharSize + mSpace;
    }
}

答案 11 :(得分:1)

public class GenericTextWatcher implements TextWatcher {
    private EditText etPrev;
    private EditText etNext;

    public GenericTextWatcher(EditText etNext, EditText etPrev) {
        this.etPrev = etPrev;
        this.etNext = etNext;
    }

    @Override
    public void afterTextChanged(Editable editable) {
        String text = editable.toString();
        if (text.length() == 1)
            etNext.requestFocus();
        else if (text.length() == 0)
            etPrev.requestFocus();
    }

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

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

接下来,我们在每个edittext上添加addTextChangedListener。

e1.addTextChangedListener(GenericTextWatcher(e2, e1))
e2.addTextChangedListener(GenericTextWatcher(e3, e1))
e3.addTextChangedListener(GenericTextWatcher(e4, e2))
e4.addTextChangedListener(GenericTextWatcher(e5, e3))
e5.addTextChangedListener(GenericTextWatcher(e6, e4))
e6.addTextChangedListener(GenericTextWatcher(e6, e5))

答案 12 :(得分:1)

使用数据绑定:

class EnterOTPDialogFragment extends Fragment {
    FragmentEnterOtpdialogBinding binding;
    GenericTextWatcher watcher1;
    GenericTextWatcher watcher2;
    GenericTextWatcher watcher3;
    GenericTextWatcher watcher4;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_enter_otpdialog, container, false);

        watcher1 = new GenericTextWatcher(binding.optDigit1);
        watcher2 = new GenericTextWatcher(binding.optDigit2);
        watcher3 = new GenericTextWatcher(binding.optDigit3);
        watcher4 = new GenericTextWatcher(binding.optDigit4);
        binding.optDigit1.addTextChangedListener(watcher1);
        binding.optDigit1.setOnKeyListener(watcher1);
        binding.optDigit2.addTextChangedListener(watcher2);
        binding.optDigit2.setOnKeyListener(watcher2);
        binding.optDigit3.addTextChangedListener(watcher3);
        binding.optDigit3.setOnKeyListener(watcher3);
        binding.optDigit4.addTextChangedListener(watcher4);
        binding.optDigit4.setOnKeyListener(watcher4);
        return binding.getRoot();
    }

    public class GenericTextWatcher implements TextWatcher, View.OnKeyListener {
        private View view;
        String previousText = "";

        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.optDigit1:
                    if (text.length() == 1) {
                        if (previousText.length() > 0) {
                            binding.optDigit1.removeTextChangedListener(watcher1);
                            binding.optDigit1.setText(previousText);
                            binding.optDigit1.addTextChangedListener(watcher1);

                            binding.optDigit2.removeTextChangedListener(watcher2);
                            binding.optDigit2.setText(text);
                            binding.optDigit2.addTextChangedListener(watcher2);
                        }
                        binding.optDigit2.requestFocus();
                    }
                    break;
                case R.id.optDigit2:
                    if (text.length() == 1) {
                        if (previousText.length() > 0) {
                            binding.optDigit2.removeTextChangedListener(watcher2);
                            binding.optDigit2.setText(previousText);
                            binding.optDigit2.addTextChangedListener(watcher2);

                            binding.optDigit3.removeTextChangedListener(watcher3);
                            binding.optDigit3.setText(text);
                            binding.optDigit3.addTextChangedListener(watcher3);

                        }
                        binding.optDigit3.requestFocus();

                    } else if (text.length() == 0)
                        binding.optDigit1.requestFocus();
                    break;
                case R.id.optDigit3:
                    if (text.length() == 1) {
                        if (previousText.length() > 0) {
                            binding.optDigit3.removeTextChangedListener(watcher3);
                            binding.optDigit3.setText(previousText);
                            binding.optDigit3.addTextChangedListener(watcher3);

                            binding.optDigit4.removeTextChangedListener(watcher4);
                            binding.optDigit4.setText(text);
                            binding.optDigit4.addTextChangedListener(watcher4);
                        }
                        binding.optDigit4.requestFocus();
                    } else if (text.length() == 0)
                        binding.optDigit2.requestFocus();
                    break;
                case R.id.optDigit4:
                    if (text.length() == 0) {

                        binding.optDigit3.requestFocus();
                    } else {
                        try {
                            final InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
                            imm.hideSoftInputFromWindow(getView().getWindowToken(), 0);
                            Log.e(TAG, "afterTextChanged: hide keyboard");
                        } catch (Exception e) {
                            Log.e(TAG, "afterTextChanged: " + e.toString());
                        }
                    }


                    break;
            }
        }

        @Override
        public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
            // TODO Auto-generated method stub
            Log.d(TAG, "beforeTextChanged: " + arg0);
            if (arg0.length() > 0) {
                previousText = arg0.toString();
            }
        }

        @Override
        public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
            // TODO Auto-generated method stub
        }

        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            previousText = "";
            Log.d(TAG, "onKey: keyCode = " + keyCode + ", event = " + event.toString());
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KEYCODE_DEL) {
                switch (view.getId()) {
                    case R.id.optDigit2:
                        if (binding.optDigit2.getText().toString().trim().length() == 0)
                            binding.optDigit1.requestFocus();
                        break;
                    case R.id.optDigit3:
                        if (binding.optDigit3.getText().toString().trim().length() == 0)
                            binding.optDigit2.requestFocus();
                        break;
                    case R.id.optDigit4:
                        if (binding.optDigit4.getText().toString().trim().length() == 0)
                            binding.optDigit3.requestFocus();
                        else if (binding.optDigit4.getText().toString().trim().length() == 1)
                            try {

                                ((BaseActivity) getActivity()).hideSoftKeyboard();
                            } catch (Exception e) {
                                Log.e(TAG, "afterTextChanged: " + e.toString());
                            }
                        break;
                }

            }
            return false;
        }
    }
}

答案 13 :(得分:1)

如果您想添加一些额外的逻辑来删除OTP,可以尝试一下,我根据A.R.的回答创建了它。 确保为所有edittext设置maxlength = 2

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.etOTP1:
                if (text.length() > 1) {
                    etOTP1.setText(String.valueOf(text.charAt(0)));
                    etOTP2.setText(String.valueOf(text.charAt(1)));
                    etOTP2.requestFocus();
                    etOTP2.setSelection(etOTP2.getText().length());
                }
                break;
            case R.id.etOTP2:
                if (text.length() > 1){
                    etOTP2.setText(String.valueOf(text.charAt(0)));
                    etOTP3.setText(String.valueOf(text.charAt(1)));
                    etOTP3.requestFocus();
                    etOTP3.setSelection(etOTP3.getText().length());
                }
                if (text.length() == 0){
                    etOTP1.requestFocus();
                    etOTP1.setSelection(etOTP1.getText().length());
                }
                break;
            case R.id.etOTP3:
                if (text.length() > 1){
                    etOTP3.setText(String.valueOf(text.charAt(0)));
                    etOTP4.setText(String.valueOf(text.charAt(1)));
                    etOTP4.requestFocus();
                    etOTP4.setSelection(etOTP4.getText().length());
                }
                if (text.length() == 0){
                    etOTP2.requestFocus();
                    etOTP2.setSelection(etOTP2.getText().length());
                }
                break;
            case R.id.etOTP4:
                if (text.length() == 0){
                    etOTP3.requestFocus();
                    etOTP3.setSelection(etOTP3.getText().length());
                }
                break;
        }
    }
    @Override
    public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
    }

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

答案 14 :(得分:1)

以下解决方案已考虑在内:

  1. 在聚焦的编辑文本中输入一位OTP时,自动聚焦到下一个编辑文本。

  2. 在聚焦的编辑文本中删除一位OTP时,自动聚焦到上一个编辑文本。

onTextChanged()和afterTextChanged()中的工作组合有助于实现相同目的。

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

答案 15 :(得分:0)

使用DataBinding布局:

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

答案 16 :(得分:0)

仅使用具有自定义背景的 EditText

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

此处ic_otp_block是矢量资产(使用SVG),它是一个矩形块,上面带有透明的圆形孔。

otp_block.png enter image description here

注意:这是一个模糊的png图片,可用于测试或将其转换为SVG。

现在在您的Java文件中:

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

输出:

enter image description here

答案 17 :(得分:0)

我在https://gist.github.com/ShivamPokhriyal/8d0cf4aef062e6c59d00c04c53e03158处创建了要点,您只需在项目中复制粘贴即可。

它将创建一个自定义的OTPEditText类,该类可在用户键入内容时处理将焦点移至下一个或上一个edittext的问题,并在用户长按otp并将其粘贴到editText时处理粘贴事件。所有这些只能在xml中完成。无需用这些东西污染您的活动。

import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * This class handles otp input in multiple edittexts.
 * It will move focus to next edittext, if available, when user enters otp.
 * And it will move focus to the previous edittext, if available, when user deletes otp.
 * It will also delegate the paste option, if user long presses and pastes a string into the otp input.
 *
 * <b>XML attributes</b>
 *
 * @attr ref your_package_name.R.styleable#OTPView_nextView
 * @attr ref your_package_name.R.styleable#OTPView_prevView
 *
 * @author $|-|!˅@M
 */
public class OTPEditText extends androidx.appcompat.widget.AppCompatEditText {

    @Nullable
    private View nextView;

    @Nullable
    private View previousView;

    // Unfortunately getParent returns null inside the constructor. So we need to store the IDs.
    private int nextViewId;
    private int previousViewId;

    @Nullable
    private Listener listener;

    private static final int NO_ID = -1;

    public interface Listener {
        void onPaste(String s);
    }

    public OTPEditText(@NonNull Context context) {
        super(context);
    }

    public OTPEditText(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public OTPEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    public void setListener(Listener listener) {
        this.listener = listener;
    }

    /**
     * Called when a context menu option for the text view is selected.  Currently
     * this will be one of {@link android.R.id#selectAll}, {@link android.R.id#cut},
     * {@link android.R.id#copy}, {@link android.R.id#paste} or {@link android.R.id#shareText}.
     *
     * @return true if the context menu item action was performed.
     */
    @Override
    public boolean onTextContextMenuItem(int id) {
        if (id == android.R.id.paste) {
            ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);

            // Examines the item on the clipboard. If getText() does not return null, the clip item contains the
            // text. Assumes that this application can only handle one item at a time.
            ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);

            // Gets the clipboard as text.
            CharSequence pasteData = item.getText();

            if (listener != null && pasteData != null) {
                listener.onPaste(pasteData.toString());
                return true;
            }
        }
        return super.onTextContextMenuItem(id);
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        // If we've gotten focus here
        if (focused && this.getText() != null) {
            this.setSelection(this.getText().length());
        }
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.OTPView, 0, 0);
        nextViewId = typedArray.getResourceId(R.styleable.OTPView_nextView, NO_ID);
        previousViewId = typedArray.getResourceId(R.styleable.OTPView_prevView, NO_ID);

        typedArray.recycle();

        this.setOnKeyListener((v, keyCode, event) -> {
            if (event.getAction()!= KeyEvent.ACTION_DOWN) {
                return true;
            }
            //You can identify which key pressed by checking keyCode value with KeyEvent.KEYCODE_
            if(keyCode == KeyEvent.KEYCODE_DEL) {
                // Back pressed. If we have a previous view. Go to it.
                if (getPreviousView() != null) {
                    getPreviousView().requestFocus();
                    return true;
                }
            }
            return false;
        });

        this.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 (s.length() == 1 && getNextView() != null) {
                    getNextView().requestFocus();
                } else if (s.length() == 0 && getPreviousView() != null) {
                    getPreviousView().requestFocus();
                }
            }
        });

        // Android 3rd party keyboards show the copied text into the suggestion box for the user.
        // Users can then simply tap on that suggestion to paste the text on the edittext.
        // But I don't know of any API that allows handling of those paste actions.
        // Below code will try to tell those keyboards to stop showing those suggestion. 
        this.setInputType(EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS | EditorInfo.TYPE_CLASS_NUMBER);
    }

    private View getNextView() {
        if (nextView != null) {
            return nextView;
        }
        if (nextViewId != NO_ID && getParent() instanceof View) {
            nextView = ((View) getParent()).findViewById(nextViewId);
            return nextView;
        }
        return null;
    }


    private View getPreviousView() {
        if (previousView != null) {
            return previousView;
        }
        if (previousViewId != NO_ID && getParent() instanceof View) {
            previousView = ((View) getParent()).findViewById(previousViewId);
            return previousView;
        }
        return null;
    }
}

要点还包括可以直接添加到活动中的xml和java代码。

答案 18 :(得分:0)

支持插入和删除的解决方案(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));
    }

答案 19 :(得分:0)

您可以继承TextWatcher并实现自己的逻辑。

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

答案 20 :(得分:0)

您可以制作自定义Editext并将其添加到xml文件中,在下面找到自定义类

Require Import Coq.Lists.List. Import ListNotations.
Require Import Coq.Sorting.Mergesort. Import NatSort.
Scheme Equality for nat.
Inductive Gen : Set :=
| BGen : nat -> nat -> Gen.

Definition g1 := BGen 1 2.
Definition g2 := BGen 2 3.


Fixpoint parents (l : list Gen) (c : nat) :=
  match l with
  | [] => []
  | (BGen p c') :: l' => if nat_beq c c'
                         then [p]
                         else parents l' c
  end.

Fixpoint deduplicate' (ls : list nat) :=
  match ls with
  | [] => []
  | x :: [] => [x]
  | x :: ((y :: ys) as xs)
    => if nat_beq x y
       then deduplicate' xs
       else x :: deduplicate' xs
  end.
Definition deduplicate (ls : list nat) := deduplicate' (sort ls).

Definition parents_step (l : list Gen) (cs : list nat) :=
  deduplicate (cs ++ List.flat_map (parents l) cs).

Fixpoint all_parents' (l : list Gen) (cs : list nat) (fuel : nat) :=
  match fuel with
  | 0 => cs
  | S fuel'
    => all_parents' l (parents_step l cs) fuel'
  end.
Definition all_parents (l : list Gen) (c : nat) :=
  deduplicate (all_parents' l (parents l c) (List.length l)).

Definition gs := (g1::g2::nil).

Compute all_parents gs 3. (* [1; 2] *)

以下参考检查链接

https://madoverandroid.wordpress.com/2017/07/11/edittext-with-separate-box-for-each-letter-customentryedittext/