Open Shusshu opened 9 years ago
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);
Cannot choose which keyboard to show