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

Mou*_*esh 22 android android-edittext

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

在此输入图像描述

Lal*_*dar 27

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

这是我在 Kotlin 中实现的解决方案,它适用于通过在键盘上按下的 Delete 键进行删除。此外,delete 函数是这样实现的,当当前EditText为空并按下 Delete 键时,它会切换回上一个EditText并删除其元素。

显示实现的 GIF

  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))
    
    Run Code Online (Sandbox Code Playgroud)
  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
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

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

  • 这应该是最好但简单的答案! (2认同)

A.R*_*.R. 21

您可以通过使TextWatcher更通用来尝试这一点,因此它易于使用和理解

使用以下课程:

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
        }
    }
Run Code Online (Sandbox Code Playgroud)

如何使用上面的课程

et1.addTextChangedListener(new GenericTextWatcher(et1));
et2.addTextChangedListener(new GenericTextWatcher(et2));
et3.addTextChangedListener(new GenericTextWatcher(et3));
et4.addTextChangedListener(new GenericTextWatcher(et4));
Run Code Online (Sandbox Code Playgroud)

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

PS你可以在这里找到一些 xml设计 GitHub,其他一些样本设计xml供参考

  • 删除怎么样? (15认同)
  • 这有一个缺点,它不会带您到上一个“EditText”,除非您在当前“EditText”中输入一个值,然后按删除键。 (2认同)

May*_*Raj 9

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) {
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们在每个编辑文本上添加 addTextChangedListener。

e1.addTextChangedListener(new GenericTextWatcher(e2, e1))
e2.addTextChangedListener(new GenericTextWatcher(e3, e1))
e3.addTextChangedListener(new GenericTextWatcher(e4, e2))
e4.addTextChangedListener(new GenericTextWatcher(e5, e3))
e5.addTextChangedListener(new GenericTextWatcher(e6, e4))
e6.addTextChangedListener(new GenericTextWatcher(e6, e5))
Run Code Online (Sandbox Code Playgroud)


小智 8

在此处输入图片说明

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

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

不要android:maxLength="1"在您的xml中使用。

//package your package

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class PinActivity extends AppCompatActivity {

    private EditText editText1, editText2, editText3, editText4;
    private EditText[] editTexts;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pin);

        editText1 = (EditText) findViewById(R.id.otpEdit1);
        editText2 = (EditText) findViewById(R.id.otpEdit2);
        editText3 = (EditText) findViewById(R.id.otpEdit3);
        editText4 = (EditText) findViewById(R.id.otpEdit4);
        editTexts = new EditText[]{editText1, editText2, editText3, editText4};

        editText1.addTextChangedListener(new PinTextWatcher(0));
        editText2.addTextChangedListener(new PinTextWatcher(1));
        editText3.addTextChangedListener(new PinTextWatcher(2));
        editText4.addTextChangedListener(new PinTextWatcher(3));

        editText1.setOnKeyListener(new PinOnKeyListener(0));
        editText2.setOnKeyListener(new PinOnKeyListener(1));
        editText3.setOnKeyListener(new PinOnKeyListener(2));
        editText4.setOnKeyListener(new PinOnKeyListener(3));
    }


    public class PinTextWatcher implements TextWatcher {

        private int currentIndex;
        private boolean isFirst = false, isLast = false;
        private String newTypedString = "";

        PinTextWatcher(int currentIndex) {
            this.currentIndex = currentIndex;

            if (currentIndex == 0)
                this.isFirst = true;
            else if (currentIndex == editTexts.length - 1)
                this.isLast = true;
        }

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

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            newTypedString = s.subSequence(start, start + count).toString().trim();
        }

        @Override
        public void afterTextChanged(Editable s) {

            String text = newTypedString;

            /* Detect paste event and set first char */
            if (text.length() > 1)
                text = String.valueOf(text.charAt(0)); // TODO: We can fill out other EditTexts

            editTexts[currentIndex].removeTextChangedListener(this);
            editTexts[currentIndex].setText(text);
            editTexts[currentIndex].setSelection(text.length());
            editTexts[currentIndex].addTextChangedListener(this);

            if (text.length() == 1)
                moveToNext();
            else if (text.length() == 0)
                moveToPrevious();
        }

        private void moveToNext() {
            if (!isLast)
                editTexts[currentIndex + 1].requestFocus();

            if (isAllEditTextsFilled() && isLast) { // isLast is optional
                editTexts[currentIndex].clearFocus();
                hideKeyboard();
            }
        }

        private void moveToPrevious() {
            if (!isFirst)
                editTexts[currentIndex - 1].requestFocus();
        }

        private boolean isAllEditTextsFilled() {
            for (EditText editText : editTexts)
                if (editText.getText().toString().trim().length() == 0)
                    return false;
            return true;
        }

        private void hideKeyboard() {
            if (getCurrentFocus() != null) {
                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
                inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            }
        }

    }

    public class PinOnKeyListener implements View.OnKeyListener {

        private int currentIndex;

        PinOnKeyListener(int currentIndex) {
            this.currentIndex = currentIndex;
        }

        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
                if (editTexts[currentIndex].getText().toString().isEmpty() && currentIndex != 0)
                    editTexts[currentIndex - 1].requestFocus();
            }
            return false;
        }

    }

}
Run Code Online (Sandbox Code Playgroud)


Kal*_*kar 6

在此处输入图片说明

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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如下所示在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"/>
Run Code Online (Sandbox Code Playgroud)

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

  • 要更改文本颜色,请使用 getPaint().setColor(Color.parseColor("#FFFFFF"))`。 (2认同)
  • 嗨,干得好。如何在这些框中显示光标闪烁? (2认同)
  • `android:cursorVisible="true"`...它不显示光标闪烁..任何帮助。尝试使用 `android:textCursorDrawable="@drawable/color_cursor"` ....但没有成功 (2认同)

Men*_*ena 6

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