Skip to content

Instantly share code, notes, and snippets.

@alphamu
Last active January 13, 2024 09:30
Show Gist options
  • Save alphamu/0d3055e0233c5749b8d6 to your computer and use it in GitHub Desktop.
Save alphamu/0d3055e0233c5749b8d6 to your computer and use it in GitHub Desktop.
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.text.Editable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
public class PinEntryEditText extends EditText {
public static final String XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android";
private float mSpace = 24; //24 dp by default, space between the lines
private float mCharSize;
private float mNumChars = 4;
private float mLineSpacing = 8; //8dp by default, height of the text from our lines
private int mMaxLength = 4;
private OnClickListener mClickListener;
private float mLineStroke = 1; //1dp by default
private float mLineStrokeSelected = 2; //2dp by default
private Paint mLinesPaint;
int[][] mStates = new int[][]{
new int[]{android.R.attr.state_selected}, // selected
new int[]{android.R.attr.state_focused}, // focused
new int[]{-android.R.attr.state_focused}, // unfocused
};
int[] mColors = new int[]{
Color.GREEN,
Color.BLACK,
Color.GRAY
};
ColorStateList mColorStates = new ColorStateList(mStates, mColors);
public PinEntryEditText(Context context) {
super(context);
}
public PinEntryEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public PinEntryEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public PinEntryEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
float multi = context.getResources().getDisplayMetrics().density;
mLineStroke = multi * mLineStroke;
mLineStrokeSelected = multi * mLineStrokeSelected;
mLinesPaint = new Paint(getPaint());
mLinesPaint.setStrokeWidth(mLineStroke);
if (!isInEditMode()) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.colorControlActivated,
outValue, true);
final int colorActivated = outValue.data;
mColors[0] = colorActivated;
context.getTheme().resolveAttribute(R.attr.colorPrimaryDark,
outValue, true);
final int colorDark = outValue.data;
mColors[1] = colorDark;
context.getTheme().resolveAttribute(R.attr.colorControlHighlight,
outValue, true);
final int colorHighlight = outValue.data;
mColors[2] = colorHighlight;
}
setBackgroundResource(0);
mSpace = multi * mSpace; //convert to pixels for our density
mLineSpacing = multi * mLineSpacing; //convert to pixels for our density
mMaxLength = attrs.getAttributeIntValue(XML_NAMESPACE_ANDROID, "maxLength", 4);
mNumChars = mMaxLength;
//Disable copy paste
super.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
public void onDestroyActionMode(ActionMode mode) {
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
});
// When tapped, move cursor to end of text.
super.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
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) {
//super.onDraw(canvas);
int availableWidth = getWidth() - getPaddingRight() - getPaddingLeft();
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++) {
updateColorForLines(i == textLength);
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;
}
}
}
private int getColorForState(int... states) {
return mColorStates.getColorForState(states, Color.GRAY);
}
/**
* @param next Is the current char the next character to be input?
*/
private void updateColorForLines(boolean next) {
if (isFocused()) {
mLinesPaint.setStrokeWidth(mLineStrokeSelected);
mLinesPaint.setColor(getColorForState(android.R.attr.state_focused));
if (next) {
mLinesPaint.setColor(getColorForState(android.R.attr.state_selected));
}
} else {
mLinesPaint.setStrokeWidth(mLineStroke);
mLinesPaint.setColor(getColorForState(-android.R.attr.state_focused));
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin"
tools:context="com.alimuzaffar.customwidgets.MainActivity">
<TextView
android:textAppearance="@android:style/TextAppearance.Large"
android:text="Please enter PIN"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.alimuzaffar.customwidgets.PinEntryEditText
android:id="@+id/txt_pin_entry"
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" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:hint="Regular EditText"/>
</LinearLayout>
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final PinEntryEditText txtPinEntry = (PinEntryEditText) findViewById(R.id.txt_pin_entry);
txtPinEntry.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.toString().equals("1234")) {
Toast.makeText(MainActivity.this, "Success", Toast.LENGTH_SHORT).show();
} else if (s.length() == "1234".length()) {
Toast.makeText(MainActivity.this, "Incorrect", Toast.LENGTH_SHORT).show();
txtPinEntry.setText(null);
}
}
});
}
}
@jjsan
Copy link

jjsan commented Nov 4, 2019

there is error getting width of character
canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2, bottom - mLineSpacing, getPaint());
should be
canvas.drawText(text, i, i + 1, middle - textWidths[i] / 2, bottom - mLineSpacing, getPaint());

@NizarETH
Copy link

NizarETH commented Nov 5, 2019

for AndroidX extend androidx.appcompat.widget.AppCompatEditText instead of Edittext

@sajidbelim99
Copy link

--To change text color following this line write in PinEntryEditText.java class
To change text color use getPaint().setColor(Color.parseColor("#FFFFFF")). You can write this on line 152.
--To change edit text underline color to write following line in PinEntryEditText
Replace colorControlActivated, colorPrimaryDark, colorControlHighlight To "colorBackgroundFloating"

@adispatil
Copy link

android:cursorVisible="true"
cursor is not visible.

@Uche01
Copy link

Uche01 commented Oct 14, 2020

How can change to a password edittext? Setting the attribute in XML or programmatically does not work

@jjsan
Copy link

jjsan commented Oct 16, 2020

How can change to a password edittext? Setting the attribute in XML or programmatically does not work
what have you tried?

Have you tried
android:inputType="password"
?

@ashishstiga
Copy link

i tried but its not working numbers are visible when input

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment