Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Edit text for OTP with Each letter in separate positions

I'm working on a application which asks for OTP when user want to reset his password for which I need a text like the one in attached Image... What I thought to proceed with is individual editText for each of the letter, All of them arranged in linear layout of horizontal orientation with some margin and max length as 1 so only one letter can be entered in each editText... Is that a right Approach?? Any Suggestions??

enter image description here

like image 883
Moulesh Avatar asked Aug 10 '16 11:08

Moulesh


People also ask

How do I get the number to edit text?

Use android:inputType="number" to force it to be numeric. Convert the resulting string into an integer (e.g., Integer. parseInt(myEditText. getText().


2 Answers

enter image description here

OtpEditText.java (Custom 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;             }         }     } } 

Use this customised EditText in your XML like below:

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

Reference:
Article: https://medium.com/@ali.muzaffar/building-a-pinentryedittext-in-android-5f2eddcae5d3
Sample Code: https://gist.github.com/alphamu/0d3055e0233c5749b8d6

like image 158
Kalpesh Wadekar Avatar answered Sep 21 '22 23:09

Kalpesh Wadekar


After all of these answers, I didn't find what I wanted as considering the UI/UX, the deletion of element was flawed in such a way that to go back to previous EditText, current EditText should not be empty.

Here's the solution I've implemented in Kotlin which works for Deletion by the Delete Key, pressed on the keyboard. Also, the delete function is implemented as such that when the current EditText is empty and Delete key is pressed, it switches back to previous EditText and delete its element also.

GIF showing the implementation

  1. Call the functions as such:

    //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. Now, paste these two classes in your current class

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

Further, to disable the visible cursor, you can either use android:cursorVisible="false" in your EditText tag in the Layout or can use the java function setCursorVisible(false).

Edit: I'm using stock widget EditTexts so if you want to display a box around them, just create a drawable layout and set it as background of EditTexts and give them a padding of 5dp. This will create a box and will make it look cooler.

like image 33
Lalit Fauzdar Avatar answered Sep 22 '22 23:09

Lalit Fauzdar