InputType is not managed
Cannot choose which keyboard to show
Maybe extends EditText instead of View to get all the benefit from EditText
An easy solution would be to override the onCreateInputConnection() method from View in theCodeInput :
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnection connection = super.onCreateInputConnection(outAttrs);
outAttrs.inputType |= InputType.TYPE_CLASS_NUMBER;
return connection;
}
But then, depending of the keyboard, user can switch to letters...
Otherwise, it could be done inside the onKeyUp() by filtering the characters (currently A-Z0-9)
imeOptions could also be supported that way... But I think it's you get more benefit by extending EditText :)
I think not extending EditText was done on purpose (to avoid handling the cursor and the whole touch input thing).
Yes I know it was on purpose but you then lose a lot of features.
I'm currently changing it to EditText and true there are some annoying things but for the moment everything is looking good
Remove cursor: setCursorVisible(false)
Hi Shusshu,
can you please tell me how we can extend to EditText.
It's not 100% perfect and was done very quickly:
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.EditText;
import com.github.glomadrian.codeinputlib.data.FixedStack;
import com.github.glomadrian.codeinputlib.model.Underline;
import java.util.regex.Pattern;
/**
* @author Adrián García Lomas
*/
public class CodeInput2 extends EditText {
private static final int DEFAULT_CODES = 6;
private static final Pattern KEYCODE_PATTERN = Pattern.compile("KEYCODE_(\\w)");
private FixedStack<Character> characters;
private Underline underlines[];
private Paint underlinePaint;
private Paint underlineSelectedPaint;
private Paint textPaint;
private Paint hintPaint;
private ValueAnimator reductionAnimator;
private ValueAnimator hintYAnimator;
private ValueAnimator hintSizeAnimator;
private float underlineReduction;
private float underlineStrokeWidth;
private float underlineWidth;
private float reduction;
private float textSize;
private float textMarginBottom;
private float hintX;
private float hintNormalSize;
private float hintSmallSize;
private float hintMarginBottom;
private float hintActualMarginBottom;
private float viewHeight;
private long animationDuration;
private int height;
private int underlineAmount;
private int underlineColor;
private int underlineSelectedColor;
private int hintColor;
private int textColor;
private boolean underlined = true;
private String hintText;
public CodeInput2(Context context) {
super(context);
init(null);
}
public CodeInput2(Context context, AttributeSet attributeset) {
super(context, attributeset);
init(attributeset);
}
public CodeInput2(Context context, AttributeSet attributeset, int defStyledAttrs) {
super(context, attributeset, defStyledAttrs);
init(attributeset);
}
private void init(AttributeSet attributeset) {
initDefaultAttributes();
initCustomAttributes(attributeset);
initDataStructures();
initPaint();
initAnimator();
initViewOptions();
setCursorVisible(false);
}
private void initDefaultAttributes() {
underlineStrokeWidth = getContext().getResources().getDimension(R.dimen.underline_stroke_width);
underlineWidth = getContext().getResources().getDimension(R.dimen.underline_width);
underlineReduction = getContext().getResources().getDimension(R.dimen.section_reduction);
textSize = getContext().getResources().getDimension(R.dimen.text_size);
textMarginBottom = getContext().getResources().getDimension(R.dimen.text_margin_bottom);
underlineColor = getContext().getResources().getColor(R.color.underline_default_color);
underlineSelectedColor = getContext().getResources().getColor(R.color.underline_selected_color);
hintColor = getContext().getResources().getColor(R.color.hintColor);
textColor = getContext().getResources().getColor(R.color.textColor);
hintMarginBottom = getContext().getResources().getDimension(R.dimen.hint_margin_bottom);
hintNormalSize = getContext().getResources().getDimension(R.dimen.hint_size);
hintSmallSize = getContext().getResources().getDimension(R.dimen.hint_small_size);
animationDuration = getContext().getResources().getInteger(R.integer.animation_duration);
viewHeight = getContext().getResources().getDimension(R.dimen.view_height);
hintX = 0;
hintActualMarginBottom = 0;
underlineAmount = DEFAULT_CODES;
reduction = 0.0F;
}
private void initCustomAttributes(AttributeSet attributeset) {
TypedArray attributes =
getContext().obtainStyledAttributes(attributeset, R.styleable.core_area);
underlineColor = attributes.getColor(R.styleable.core_area_underline_color, underlineColor);
underlineSelectedColor =
attributes.getColor(R.styleable.core_area_underline_selected_color, underlineSelectedColor);
hintColor = attributes.getColor(R.styleable.core_area_underline_color, hintColor);
hintText = attributes.getString(R.styleable.core_area_hint_text);
underlineAmount = attributes.getInt(R.styleable.core_area_codes, underlineAmount);
textColor = attributes.getInt(R.styleable.core_area_text_color, textColor);
attributes.recycle();
}
private void initDataStructures() {
underlines = new Underline[underlineAmount];
characters = new FixedStack();
characters.setMaxSize(underlineAmount);
}
private void initPaint() {
underlinePaint = new Paint();
underlinePaint.setColor(underlineColor);
underlinePaint.setStrokeWidth(underlineStrokeWidth);
underlinePaint.setStyle(android.graphics.Paint.Style.STROKE);
underlineSelectedPaint = new Paint();
underlineSelectedPaint.setColor(underlineSelectedColor);
underlineSelectedPaint.setStrokeWidth(underlineStrokeWidth);
underlineSelectedPaint.setStyle(android.graphics.Paint.Style.STROKE);
textPaint = new Paint();
textPaint.setTextSize(textSize);
textPaint.setColor(textColor);
textPaint.setAntiAlias(true);
textPaint.setTextAlign(Paint.Align.CENTER);
hintPaint = new Paint();
hintPaint = new Paint();
hintPaint.setTextSize(hintNormalSize);
hintPaint.setAntiAlias(true);
hintPaint.setColor(underlineColor);
}
private void initAnimator() {
reductionAnimator = ValueAnimator.ofFloat(0, underlineReduction);
reductionAnimator.setDuration(animationDuration);
reductionAnimator.addUpdateListener(new ReductionAnimatorListener());
reductionAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
hintSizeAnimator = ValueAnimator.ofFloat(hintNormalSize, hintSmallSize);
hintSizeAnimator.setDuration(animationDuration);
hintSizeAnimator.addUpdateListener(new HintSizeAnimatorListener());
hintSizeAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
hintYAnimator = ValueAnimator.ofFloat(0, hintMarginBottom);
hintYAnimator.setDuration(animationDuration);
hintYAnimator.addUpdateListener(new HintYAnimatorListener());
hintYAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
}
private void initViewOptions() {
setFocusable(true);
setFocusableInTouchMode(true);
}
@Override
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
if (!gainFocus && characters.size() == 0) {
reverseAnimation();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, (int) viewHeight, oldw, oldh);
height = h;
initUnderline();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), (int) viewHeight);
}
private void initUnderline() {
for (int i = 0; i < underlineAmount; i++) {
underlines[i] = createPath(i, underlineWidth);
}
}
private Underline createPath(int position, float sectionWidth) {
float fromX = sectionWidth * (float) position;
return new Underline(fromX, height, fromX + sectionWidth, height);
}
private void startAnimation() {
reductionAnimator.start();
hintSizeAnimator.start();
hintYAnimator.start();
underlined = false;
}
private void reverseAnimation() {
reductionAnimator.reverse();
hintSizeAnimator.reverse();
hintYAnimator.reverse();
underlined = true;
}
// /**
// * Detects the del key and delete the numbers
// */
// @Override
// public boolean onKeyDown(int keyCode, KeyEvent keyevent) {
// if (keyCode == KeyEvent.KEYCODE_DEL && characters.size() != 0) {
// characters.pop();
// }
// return super.onKeyDown(keyCode, keyevent);
// }
//
// /**
// * Capture the keyboard events but only if are A-Z 0-9
// */
// @Override
// public boolean onKeyUp(int keyCode, KeyEvent keyevent) {
// String text = KeyEvent.keyCodeToString(keyCode);
// Matcher matcher = KEYCODE_PATTERN.matcher(text);
// if (matcher.matches()) {
// char character = matcher.group(1).charAt(0);
// characters.push(character);
// return true;
// } else {
// return false;
// }
// }
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
if (characters != null) {
if (underlined) {
startAnimation();
}
characters.clear();
for (int i = 0; i < text.length(); i++) {
char character = text.charAt(i);
characters.push(character);
}
invalidate();
}
}
/**
* When a touch is detected the view need to focus and animate if is necessary
*/
@Override
public boolean onTouchEvent(MotionEvent motionevent) {
if (motionevent.getAction() == 0) {
// requestFocus();
if (underlined) {
startAnimation();
}
}
return super.onTouchEvent(motionevent);
}
@Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < underlines.length; i++) {
Underline sectionpath = underlines[i];
float fromX = sectionpath.getFromX() + reduction;
float fromY = sectionpath.getFromY();
float toX = sectionpath.getToX() - reduction;
float toY = sectionpath.getToY();
drawSection(i, fromX, fromY, toX, toY, canvas);
if (characters.toArray().length > i && characters.size() != 0) {
drawCharacter(fromX, toX, characters.get(i), canvas);
}
}
if (hintText != null) {
drawHint(canvas);
}
}
private void drawSection(int position, float fromX, float fromY, float toX, float toY,
Canvas canvas) {
Paint paint = underlinePaint;
if (position == characters.size() && !underlined) {
paint = underlineSelectedPaint;
}
canvas.drawLine(fromX, fromY, toX, toY, paint);
}
private void drawCharacter(float fromX, float toX, Character character, Canvas canvas) {
float actualWidth = toX - fromX;
float centerWidth = actualWidth / 2;
float centerX = fromX + centerWidth;
canvas.drawText(character.toString(), centerX, height - textMarginBottom, textPaint);
}
private void drawHint(Canvas canvas) {
canvas.drawText(hintText, hintX, height - textMarginBottom - hintActualMarginBottom, hintPaint);
}
public Character[] getCode() {
return characters.toArray(new Character[underlineAmount]);
}
/**
* Listener to update the reduction of the underline bars
*/
private class ReductionAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
public void onAnimationUpdate(ValueAnimator valueanimator) {
float value = ((Float) valueanimator.getAnimatedValue()).floatValue();
reduction = value;
invalidate();
}
}
/**
* Listener to update the hint y values
*/
private class HintYAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
hintActualMarginBottom = (float) animation.getAnimatedValue();
invalidate();
}
}
/**
* Listener to update the size of the hint text
*/
private class HintSizeAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float size = (float) animation.getAnimatedValue();
hintPaint.setTextSize(size);
invalidate();
}
}
}
thanks its works for me .you have saved my time.
@Sushil21 Copy paste does not work with that class. if you get it working properly let me know ;)
you need to change in xml file also like below:
<utils.CodeInput2
android:id="@+id/et_otp_code"
android:layout_marginTop="35dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:underline_color="#000000"
app:underline_selected_color="#000000"
app:text_color="#000000"
app:hint_color="#000000"
app:hint_text="Enter verification code"
app:codes="6"
android:layout_gravity="center_horizontal"
android:layout_weight="2"
android:maxLength="6"
android:background="@android:color/transparent"
/>
NOTE: utils is package name and CodeInput2 is class name.
@Sushil21 I meant the copy/paste feature in android does not work for that component
@Shusshu have you solved it ? i only found the way to intercept cut/copy/paste effect but cannot disable the show of clipboard
@Override
public boolean onTextContextMenuItem(int id) {
return true;//拦截剪切粘贴事件 consume cut/copy/paste
}
I haven't looked more into it...
If you want to use copy and paste functions, you just need to write this code. codeInputEditText.setOnLongClickListener(view -> false);