diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 00000000..e8affc44 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,176 @@ + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml index c9ad7b12..a31bd251 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -5,7 +5,6 @@ - diff --git a/MaterialEditText.iml b/MaterialEditText.iml index 89d1d46a..5133111f 100644 --- a/MaterialEditText.iml +++ b/MaterialEditText.iml @@ -14,7 +14,7 @@ - + diff --git a/README.md b/README.md index 46b50027..7e3de311 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,10 @@ AppCompat v21 makes it easy to use Material Design EditText in our apps, but it' highlight: ![HighlightFloatingLabel](./images/highlight.jpg) + + custom floating label text: + + ![CustomFloatingLabelText](./images/custom_floating_label_text.png) 5. Single Line Ellipsis @@ -54,12 +58,6 @@ AppCompat v21 makes it easy to use Material Design EditText in our apps, but it' ![CustomAccentTypeface](./images/custom_accent_typeface.png) -9. Custom floating label text - - defaults is the hint value, but can be customized: - - ![DefaultHint](./images/custom_floating_label.png) - ## Sample [MaterialEditText-1.4.3-sample.apk](https://github.com/rengwuxian/MaterialEditText/releases/download/1.4.3/MaterialEditText-1.4.3-sample.apk) diff --git a/images/custom_accent_typeface.png b/images/custom_accent_typeface.png index ce87c943..4de79d40 100644 Binary files a/images/custom_accent_typeface.png and b/images/custom_accent_typeface.png differ diff --git a/images/custom_floating_label.png b/images/custom_floating_label.png deleted file mode 100644 index c3fab044..00000000 Binary files a/images/custom_floating_label.png and /dev/null differ diff --git a/images/custom_floating_label_text.png b/images/custom_floating_label_text.png new file mode 100644 index 00000000..b3553975 Binary files /dev/null and b/images/custom_floating_label_text.png differ diff --git a/library/src/main/java/com/rengwuxian/materialedittext/MaterialAutoCompleteTextView.java b/library/src/main/java/com/rengwuxian/materialedittext/MaterialAutoCompleteTextView.java index e82dfba9..1e74b1e0 100644 --- a/library/src/main/java/com/rengwuxian/materialedittext/MaterialAutoCompleteTextView.java +++ b/library/src/main/java/com/rengwuxian/materialedittext/MaterialAutoCompleteTextView.java @@ -18,6 +18,7 @@ import android.view.View; import android.widget.AutoCompleteTextView; import android.content.res.ColorStateList; + import com.nineoldandroids.animation.ArgbEvaluator; import com.nineoldandroids.animation.ObjectAnimator; @@ -31,602 +32,603 @@ *

*/ public class MaterialAutoCompleteTextView extends AutoCompleteTextView { - public static final int FLOATING_LABEL_NONE = 0; - public static final int FLOATING_LABEL_NORMAL = 1; - public static final int FLOATING_LABEL_HIGHLIGHT = 2; - - /** - * the spacing between the main text and the inner top padding. - */ - private int extraPaddingTop; - - /** - * the spacing between the main text and the inner bottom padding. - */ - private int extraPaddingBottom; - - /** - * the floating label's text size. - */ - private final int floatingLabelTextSize; - - /** - * the spacing between the main text and the inner components (floating label, bottom ellipsis, characters counter). - */ - private final int innerComponentsSpacing; - - /** - * whether the floating label should be shown. default is false. - */ - private boolean floatingLabelEnabled; - - /** - * whether to highlight the floating label's text color when focused (with the main color). default is true. - */ - private boolean highlightFloatingLabel; - - /** - * the base color of the line and the texts. default is black. - */ - private int baseColor; - - /** - * inner top padding - */ - private int innerPaddingTop; - - /** - * inner bottom padding - */ - private int innerPaddingBottom; - - /** - * the underline's highlight color, and the highlight color of the floating label if app:highlightFloatingLabel is set true in the xml. default is black(when app:darkTheme is false) or white(when app:darkTheme is true) - */ - private int primaryColor; - - /** - * the color for when something is wrong.(e.g. exceeding max characters) - */ - private int errorColor; - - /** - * characters count limit. 0 means no limit. default is 0. NOTE: the character counter will increase the View's height. - */ - private int maxCharacters; - - /** - * whether to show the bottom ellipsis in singleLine mode. default is false. NOTE: the bottom ellipsis will increase the View's height. - */ - private boolean singleLineEllipsis; - - /** - * bottom ellipsis's height - */ - private final int bottomEllipsisSize; - - /** - * If the MaterialEditText always extends its bottom space. If it's true, the MaterialEditText always extends some space at the bottom; and if it's false, the MaterialEditText only extends some space when needed (e.g. when an error text is being shown, and there's no extra space at the bottom). - */ - private boolean extendBottom; - - /** - * Helper text at the bottom - */ - private String helperText; - - /** - * error text for manually invoked {@link #setError(CharSequence)} - */ - private String tempErrorText; - - /** - * animation fraction of the floating label (0 as totally hidden). - */ - private float floatingLabelFraction; - - /** - * whether the floating label is being shown. - */ - private boolean floatingLabelShown; - - /** - * the floating label's focusFraction - */ - private float focusFraction; - - /** - * The font used for the accent texts (floating label, error/helper text, character counter, etc.) - */ - private Typeface accentTypeface; - - /** - * Text for the floatLabel if different from the hint - */ - private CharSequence floatingLabelText; - - private ArgbEvaluator focusEvaluator = new ArgbEvaluator(); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - ObjectAnimator labelAnimator; - ObjectAnimator labelFocusAnimator; - OnFocusChangeListener interFocusChangeListener; - OnFocusChangeListener outerFocusChangeListener; - - public MaterialAutoCompleteTextView(Context context) { - this(context, null); - } - - public MaterialAutoCompleteTextView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public MaterialAutoCompleteTextView(Context context, AttributeSet attrs, int style) { - super(context, attrs, style); - - setFocusable(true); - setFocusableInTouchMode(true); - setClickable(true); - - floatingLabelTextSize = getResources().getDimensionPixelSize(R.dimen.floating_label_text_size); - innerComponentsSpacing = getResources().getDimensionPixelSize(R.dimen.inner_components_spacing); - bottomEllipsisSize = getResources().getDimensionPixelSize(R.dimen.bottom_ellipsis_height); - - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialEditText); - baseColor = typedArray.getColor(R.styleable.MaterialEditText_baseColor, Color.BLACK); - ColorStateList colorStateList = new ColorStateList(new int[][] {new int[] {android.R.attr.state_enabled}, EMPTY_STATE_SET}, new int[] {baseColor & 0x00ffffff | 0xdf000000, baseColor & 0x00ffffff | 0x44000000}); - setTextColor(colorStateList); - - primaryColor = typedArray.getColor(R.styleable.MaterialEditText_primaryColor, baseColor); - setFloatingLabelInternal(typedArray.getInt(R.styleable.MaterialEditText_floatingLabel, 0)); - errorColor = typedArray.getColor(R.styleable.MaterialEditText_errorColor, Color.parseColor("#e7492E")); - maxCharacters = typedArray.getInt(R.styleable.MaterialEditText_maxCharacters, 0); - singleLineEllipsis = typedArray.getBoolean(R.styleable.MaterialEditText_singleLineEllipsis, false); - helperText = typedArray.getString(R.styleable.MaterialEditText_helperText); - extendBottom = typedArray.getBoolean(R.styleable.MaterialEditText_extendBottom, false) || helperText != null || maxCharacters > 0 || singleLineEllipsis; - String fontPath = typedArray.getString(R.styleable.MaterialEditText_accentTypeface); - if (fontPath != null) { - accentTypeface = getCustomTypeface(fontPath); - paint.setTypeface(accentTypeface); + public static final int FLOATING_LABEL_NONE = 0; + public static final int FLOATING_LABEL_NORMAL = 1; + public static final int FLOATING_LABEL_HIGHLIGHT = 2; + + /** + * the spacing between the main text and the inner top padding. + */ + private int extraPaddingTop; + + /** + * the spacing between the main text and the inner bottom padding. + */ + private int extraPaddingBottom; + + /** + * the floating label's text size. + */ + private final int floatingLabelTextSize; + + /** + * the spacing between the main text and the inner components (floating label, bottom ellipsis, characters counter). + */ + private final int innerComponentsSpacing; + + /** + * whether the floating label should be shown. default is false. + */ + private boolean floatingLabelEnabled; + + /** + * whether to highlight the floating label's text color when focused (with the main color). default is true. + */ + private boolean highlightFloatingLabel; + + /** + * the base color of the line and the texts. default is black. + */ + private int baseColor; + + /** + * inner top padding + */ + private int innerPaddingTop; + + /** + * inner bottom padding + */ + private int innerPaddingBottom; + + /** + * the underline's highlight color, and the highlight color of the floating label if app:highlightFloatingLabel is set true in the xml. default is black(when app:darkTheme is false) or white(when app:darkTheme is true) + */ + private int primaryColor; + + /** + * the color for when something is wrong.(e.g. exceeding max characters) + */ + private int errorColor; + + /** + * characters count limit. 0 means no limit. default is 0. NOTE: the character counter will increase the View's height. + */ + private int maxCharacters; + + /** + * whether to show the bottom ellipsis in singleLine mode. default is false. NOTE: the bottom ellipsis will increase the View's height. + */ + private boolean singleLineEllipsis; + + /** + * bottom ellipsis's height + */ + private final int bottomEllipsisSize; + + /** + * If the MaterialEditText always extends its bottom space. If it's true, the MaterialEditText always extends some space at the bottom; and if it's false, the MaterialEditText only extends some space when needed (e.g. when an error text is being shown, and there's no extra space at the bottom). + */ + private boolean extendBottom; + + /** + * Helper text at the bottom + */ + private String helperText; + + /** + * error text for manually invoked {@link #setError(CharSequence)} + */ + private String tempErrorText; + + /** + * animation fraction of the floating label (0 as totally hidden). + */ + private float floatingLabelFraction; + + /** + * whether the floating label is being shown. + */ + private boolean floatingLabelShown; + + /** + * the floating label's focusFraction + */ + private float focusFraction; + + /** + * The font used for the accent texts (floating label, error/helper text, character counter, etc.) + */ + private Typeface accentTypeface; + + /** + * Text for the floatLabel if different from the hint + */ + private CharSequence floatingLabelText; + + private ArgbEvaluator focusEvaluator = new ArgbEvaluator(); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + ObjectAnimator labelAnimator; + ObjectAnimator labelFocusAnimator; + OnFocusChangeListener interFocusChangeListener; + OnFocusChangeListener outerFocusChangeListener; + + public MaterialAutoCompleteTextView(Context context) { + this(context, null); + } + + public MaterialAutoCompleteTextView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public MaterialAutoCompleteTextView(Context context, AttributeSet attrs, int style) { + super(context, attrs, style); + + setFocusable(true); + setFocusableInTouchMode(true); + setClickable(true); + + floatingLabelTextSize = getResources().getDimensionPixelSize(R.dimen.floating_label_text_size); + innerComponentsSpacing = getResources().getDimensionPixelSize(R.dimen.inner_components_spacing); + bottomEllipsisSize = getResources().getDimensionPixelSize(R.dimen.bottom_ellipsis_height); + + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialEditText); + baseColor = typedArray.getColor(R.styleable.MaterialEditText_baseColor, Color.BLACK); + ColorStateList colorStateList = new ColorStateList(new int[][]{new int[]{android.R.attr.state_enabled}, EMPTY_STATE_SET}, new int[]{baseColor & 0x00ffffff | 0xdf000000, baseColor & 0x00ffffff | 0x44000000}); + setTextColor(colorStateList); + + primaryColor = typedArray.getColor(R.styleable.MaterialEditText_primaryColor, baseColor); + setFloatingLabelInternal(typedArray.getInt(R.styleable.MaterialEditText_floatingLabel, 0)); + errorColor = typedArray.getColor(R.styleable.MaterialEditText_errorColor, Color.parseColor("#e7492E")); + maxCharacters = typedArray.getInt(R.styleable.MaterialEditText_maxCharacters, 0); + singleLineEllipsis = typedArray.getBoolean(R.styleable.MaterialEditText_singleLineEllipsis, false); + helperText = typedArray.getString(R.styleable.MaterialEditText_helperText); + extendBottom = typedArray.getBoolean(R.styleable.MaterialEditText_extendBottom, false) || helperText != null || maxCharacters > 0 || singleLineEllipsis; + String fontPath = typedArray.getString(R.styleable.MaterialEditText_accentTypeface); + if (fontPath != null) { + accentTypeface = getCustomTypeface(fontPath); + paint.setTypeface(accentTypeface); + } + floatingLabelText = typedArray.getString(R.styleable.MaterialEditText_floatingLabelText); + if (floatingLabelText == null) { + floatingLabelText = getHint(); + } + typedArray.recycle(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + setBackground(null); + } else { + setBackgroundDrawable(null); + } + if (singleLineEllipsis) { + TransformationMethod transformationMethod = getTransformationMethod(); + setSingleLine(); + setTransformationMethod(transformationMethod); + } + initPadding(); + initText(); + initFloatingLabel(); + initErrorTextListener(); + } + + private void initText() { + if (!TextUtils.isEmpty(getText())) { + CharSequence text = getText(); + setText(null); + setHintTextColor(baseColor & 0x00ffffff | 0x44000000); + setText(text); + floatingLabelFraction = 1; + floatingLabelShown = true; + } else { + setHintTextColor(baseColor & 0x00ffffff | 0x44000000); + } + } + + private void initErrorTextListener() { + 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) { + tempErrorText = null; + invalidate(); + } + }); + } + + private Typeface getCustomTypeface(@NonNull String fontPath) { + return Typeface.createFromAsset(getContext().getAssets(), fontPath); + } + + public float getFloatingLabelFraction() { + return floatingLabelFraction; + } + + public void setFloatingLabelFraction(float floatingLabelFraction) { + this.floatingLabelFraction = floatingLabelFraction; + invalidate(); + } + + public float getFocusFraction() { + return focusFraction; + } + + public void setFocusFraction(float focusFraction) { + this.focusFraction = focusFraction; + invalidate(); + } + + @Nullable + public Typeface getAccentTypeface() { + return accentTypeface; + } + + /** + * Set typeface used for the accent texts (floating label, error/helper text, character counter, etc.) + */ + public void setAccentTypeface(Typeface accentTypeface) { + this.accentTypeface = accentTypeface; + this.paint.setTypeface(accentTypeface); + postInvalidate(); + } + + public CharSequence getFloatingLabelText() { + return floatingLabelText; + } + + /** + * Set the floating label text. + *

+ * Pass null to force fallback to use hint's value. + * + * @param floatingLabelText + */ + public void setFloatingLabelText(@Nullable CharSequence floatingLabelText) { + this.floatingLabelText = floatingLabelText == null ? getHint() : floatingLabelText; + postInvalidate(); + } + + + private int getPixel(int dp) { + return Density.dp2px(getContext(), dp); + } + + private void initPadding() { + int paddingTop = getPaddingTop() - extraPaddingTop; + int paddingBottom = getPaddingBottom() - extraPaddingBottom; + extraPaddingTop = floatingLabelEnabled ? floatingLabelTextSize + innerComponentsSpacing : innerComponentsSpacing; + extraPaddingBottom = extendBottom ? floatingLabelTextSize : 0; + extraPaddingBottom += innerComponentsSpacing * 2; + setPaddings(getPaddingLeft(), paddingTop, getPaddingRight(), paddingBottom); + } + + /** + * use {@link #setPaddings(int, int, int, int)} instead, or the paddingTop and the paddingBottom may be set incorrectly. + */ + @Deprecated + @Override + public final void setPadding(int left, int top, int right, int bottom) { + super.setPadding(left, top, right, bottom); + } + + /** + * Use this method instead of {@link #setPadding(int, int, int, int)} to automatically set the paddingTop and the paddingBottom correctly. + */ + public void setPaddings(int left, int top, int right, int bottom) { + innerPaddingTop = top; + innerPaddingBottom = bottom; + super.setPadding(left, top + extraPaddingTop, right, bottom + extraPaddingBottom); + } + + /** + * get inner top padding, not the real paddingTop + */ + public int getInnerPaddingTop() { + return innerPaddingTop; + } + + /** + * get inner bottom padding, not the real paddingBottom + */ + public int getInnerPaddingBottom() { + return innerPaddingBottom; + } + + private void initFloatingLabel() { + if (floatingLabelEnabled) { + // observe the text changing + addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { } - floatingLabelText = typedArray.getString(R.styleable.MaterialEditText_floatingLabelText); - if (floatingLabelText == null) { - floatingLabelText = getHint(); + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + if (s.length() == 0) { + if (floatingLabelShown) { + floatingLabelShown = false; + getLabelAnimator().reverse(); + } + } else if (!floatingLabelShown) { + floatingLabelShown = true; + if (getLabelAnimator().isStarted()) { + getLabelAnimator().reverse(); + } else { + getLabelAnimator().start(); + } + } } - typedArray.recycle(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - setBackground(null); - } else { - setBackgroundDrawable(null); - } - if (singleLineEllipsis) { - TransformationMethod transformationMethod = getTransformationMethod(); - setSingleLine(); - setTransformationMethod(transformationMethod); - } - initPadding(); - initText(); - initFloatingLabel(); - initErrorTextListener(); - } - - private void initText() { - if (!TextUtils.isEmpty(getText())) { - CharSequence text = getText(); - setText(null); - setHintTextColor(baseColor & 0x00ffffff | 0x44000000); - setText(text); - floatingLabelFraction = 1; - floatingLabelShown = true; - } else { - setHintTextColor(baseColor & 0x00ffffff | 0x44000000); - } - } - - private void initErrorTextListener() { - 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) { - tempErrorText = null; - invalidate(); - } - }); - } - - private Typeface getCustomTypeface(@NonNull String fontPath) { - return Typeface.createFromAsset(getContext().getAssets(), fontPath); + }); + if (highlightFloatingLabel) { + // observe the focus state to animate the floating label's text color appropriately + interFocusChangeListener = new OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + if (getLabelFocusAnimator().isStarted()) { + getLabelFocusAnimator().reverse(); + } else { + getLabelFocusAnimator().start(); + } + } else { + getLabelFocusAnimator().reverse(); + } + if (outerFocusChangeListener != null) { + outerFocusChangeListener.onFocusChange(v, hasFocus); + } + } + }; + super.setOnFocusChangeListener(interFocusChangeListener); + } } - public float getFloatingLabelFraction() { - return floatingLabelFraction; - } + } + + public void setBaseColor(int color) { + baseColor = color; + postInvalidate(); + } + + public void setPrimaryColor(int color) { + primaryColor = color; + postInvalidate(); + } + + private void setFloatingLabelInternal(int mode) { + switch (mode) { + case FLOATING_LABEL_NORMAL: + floatingLabelEnabled = true; + highlightFloatingLabel = false; + break; + case FLOATING_LABEL_HIGHLIGHT: + floatingLabelEnabled = true; + highlightFloatingLabel = true; + break; + default: + floatingLabelEnabled = false; + highlightFloatingLabel = false; + break; + } + } - public void setFloatingLabelFraction(float floatingLabelFraction) { - this.floatingLabelFraction = floatingLabelFraction; - invalidate(); - } + public void setFloatingLabel(int mode) { + setFloatingLabelInternal(mode); + postInvalidate(); + } + + public void setSingleLineEllipsis() { + setSingleLineEllipsis(true); + } + + public void setSingleLineEllipsis(boolean enabled) { + singleLineEllipsis = enabled; + if (enabled) { + extendBottom(); + } + postInvalidate(); + } - public float getFocusFraction() { - return focusFraction; - } + public int getMaxCharacters() { + return maxCharacters; + } - public void setFocusFraction(float focusFraction) { - this.focusFraction = focusFraction; - invalidate(); - } + public void setMaxCharacters(int max) { + maxCharacters = max; + if (max > 0) { + extendBottom(); + } + postInvalidate(); + } + + public void setErrorColor(int color) { + errorColor = color; + postInvalidate(); + } + + public void setHelperText(CharSequence helperText) { + this.helperText = helperText == null ? null : helperText.toString(); + extendBottom(); + postInvalidate(); + } + + public String getHelperText() { + return helperText; + } + + @Override + public void setError(CharSequence errorText) { + extendBottom(); + tempErrorText = errorText == null ? null : errorText.toString(); + postInvalidate(); + } + + @Override + public CharSequence getError() { + return tempErrorText; + } + + /** + * if {@link #extendBottom} is false, set it true and reset the paddings. + */ + private void extendBottom() { + if (!extendBottom) { + extendBottom = true; + initPadding(); + } + } + + /** + * only used to draw the bottom line + */ + private boolean isInternalValid() { + return tempErrorText == null && isMaxCharactersValid(); + } + + /** + * if the main text matches the regex + */ + public boolean isValid(String regex) { + if (regex == null) { + return false; + } + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(getText()); + return matcher.matches(); + } + + /** + * check if the main text matches the regex, and set the error text if not. + * + * @return true if it matches the regex, false if not. + */ + public boolean validate(String regex, CharSequence errorText) { + boolean isValid = isValid(regex); + if (!isValid) { + setError(errorText); + } + postInvalidate(); + return isValid; + } + + @Override + public void setOnFocusChangeListener(OnFocusChangeListener listener) { + if (interFocusChangeListener == null) { + super.setOnFocusChangeListener(listener); + } else { + outerFocusChangeListener = listener; + } + } - @Nullable - public Typeface getAccentTypeface() { - return accentTypeface; + private ObjectAnimator getLabelAnimator() { + if (labelAnimator == null) { + labelAnimator = ObjectAnimator.ofFloat(this, "floatingLabelFraction", 0f, 1f); } + return labelAnimator; + } - /** - * Set typeface used for the accent texts (floating label, error/helper text, character counter, etc.) - */ - public void setAccentTypeface(Typeface accentTypeface) { - this.accentTypeface = accentTypeface; - this.paint.setTypeface(accentTypeface); - postInvalidate(); + private ObjectAnimator getLabelFocusAnimator() { + if (labelFocusAnimator == null) { + labelFocusAnimator = ObjectAnimator.ofFloat(this, "focusFraction", 0f, 1f); + } + return labelFocusAnimator; + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + // set the textSize + paint.setTextSize(floatingLabelTextSize); + + float lineStartY = getScrollY() + getHeight() - getPaddingBottom() + innerComponentsSpacing; + + // draw the background + if (!isInternalValid()) { // not valid + paint.setColor(errorColor); + canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(2), paint); + } else if (!isEnabled()) { // disabled + paint.setColor(baseColor & 0x00ffffff | 0x44000000); + float interval = getPixel(1); + for (float startX = 0; startX < getWidth(); startX += interval * 3) { + canvas.drawRect(getScrollX() + startX, lineStartY, getScrollX() + startX + interval, lineStartY + getPixel(1), paint); + } + } else if (hasFocus()) { // focused + paint.setColor(primaryColor); + canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(2), paint); + } else { // normal + paint.setColor(baseColor); + canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(1), paint); } - public CharSequence getFloatingLabelText() { - return floatingLabelText; + Paint.FontMetrics fontMetrics = paint.getFontMetrics(); + float relativeHeight = -fontMetrics.ascent - fontMetrics.descent; + + // draw the characters counter + if (maxCharacters > 0) { + paint.setColor(isMaxCharactersValid() ? getCurrentHintTextColor() : errorColor); + String text = getText().length() + " / " + maxCharacters; + canvas.drawText(text, getWidth() + getScrollX() - paint.measureText(text), lineStartY + innerComponentsSpacing + relativeHeight, paint); } - /** - * Set the floating label text. - * - * Pass null to force fallback to use hint's value. - * - * @param floatingLabelText - */ - public void setFloatingLabelText(@Nullable CharSequence floatingLabelText) { - this.floatingLabelText = floatingLabelText == null ? getHint() : floatingLabelText; - postInvalidate(); + // draw the bottom text + float bottomTextStartX = getScrollX() + (singleLineEllipsis ? (bottomEllipsisSize * 5 + getPixel(4)) : 0); + if (tempErrorText != null) { // regex error + paint.setColor(errorColor); + canvas.drawText(tempErrorText, bottomTextStartX, lineStartY + innerComponentsSpacing + relativeHeight, paint); + } else if (!TextUtils.isEmpty(helperText)) { + paint.setColor(getCurrentHintTextColor()); + canvas.drawText(helperText, bottomTextStartX, lineStartY + innerComponentsSpacing + relativeHeight, paint); } + // draw the floating label + if (floatingLabelEnabled && !TextUtils.isEmpty(floatingLabelText)) { + // calculate the text color + paint.setColor((Integer) focusEvaluator.evaluate(focusFraction, getCurrentHintTextColor(), primaryColor)); - private int getPixel(int dp) { - return Density.dp2px(getContext(), dp); - } - - private void initPadding() { - int paddingTop = getPaddingTop() - extraPaddingTop; - int paddingBottom = getPaddingBottom() - extraPaddingBottom; - extraPaddingTop = floatingLabelEnabled ? floatingLabelTextSize + innerComponentsSpacing : innerComponentsSpacing; - extraPaddingBottom = extendBottom ? floatingLabelTextSize : 0; - extraPaddingBottom += innerComponentsSpacing * 2; - setPaddings(getPaddingLeft(), paddingTop, getPaddingRight(), paddingBottom); - } - - /** - * use {@link #setPaddings(int, int, int, int)} instead, or the paddingTop and the paddingBottom may be set incorrectly. - */ - @Deprecated - @Override - public final void setPadding(int left, int top, int right, int bottom) { - super.setPadding(left, top, right, bottom); - } - - /** - * Use this method instead of {@link #setPadding(int, int, int, int)} to automatically set the paddingTop and the paddingBottom correctly. - */ - public void setPaddings(int left, int top, int right, int bottom) { - innerPaddingTop = top; - innerPaddingBottom = bottom; - super.setPadding(left, top + extraPaddingTop, right, bottom + extraPaddingBottom); - } - - /** - * get inner top padding, not the real paddingTop - */ - public int getInnerPaddingTop() { - return innerPaddingTop; - } - - /** - * get inner bottom padding, not the real paddingBottom - */ - public int getInnerPaddingBottom() { - return innerPaddingBottom; - } - - private void initFloatingLabel() { - if (floatingLabelEnabled) { - // observe the text changing - 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.length() == 0) { - if (floatingLabelShown) { - floatingLabelShown = false; - getLabelAnimator().reverse(); - } - } else if (!floatingLabelShown) { - floatingLabelShown = true; - if (getLabelAnimator().isStarted()) { - getLabelAnimator().reverse(); - } else { - getLabelAnimator().start(); - } - } - } - }); - if (highlightFloatingLabel) { - // observe the focus state to animate the floating label's text color appropriately - interFocusChangeListener = new OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) { - if (getLabelFocusAnimator().isStarted()) { - getLabelFocusAnimator().reverse(); - } else { - getLabelFocusAnimator().start(); - } - } else { - getLabelFocusAnimator().reverse(); - } - if (outerFocusChangeListener != null) { - outerFocusChangeListener.onFocusChange(v, hasFocus); - } - } - }; - super.setOnFocusChangeListener(interFocusChangeListener); - } - } - - } - - public void setBaseColor(int color) { - baseColor = color; - postInvalidate(); - } - - public void setPrimaryColor(int color) { - primaryColor = color; - postInvalidate(); - } - - private void setFloatingLabelInternal(int mode) { - switch (mode) { - case FLOATING_LABEL_NORMAL: - floatingLabelEnabled = true; - highlightFloatingLabel = false; - break; - case FLOATING_LABEL_HIGHLIGHT: - floatingLabelEnabled = true; - highlightFloatingLabel = true; - break; - default: - floatingLabelEnabled = false; - highlightFloatingLabel = false; - break; - } - } - - public void setFloatingLabel(int mode) { - setFloatingLabelInternal(mode); - postInvalidate(); - } - - public void setSingleLineEllipsis() { - setSingleLineEllipsis(true); - } - - public void setSingleLineEllipsis(boolean enabled) { - singleLineEllipsis = enabled; - if (enabled) { - extendBottom(); - } - postInvalidate(); - } - - public int getMaxCharacters() { - return maxCharacters; - } - - public void setMaxCharacters(int max) { - maxCharacters = max; - if (max > 0) { - extendBottom(); - } - postInvalidate(); - } - - public void setErrorColor(int color) { - errorColor = color; - postInvalidate(); - } - - public void setHelperText(CharSequence helperText) { - this.helperText = helperText == null ? null : helperText.toString(); - extendBottom(); - postInvalidate(); - } - - public String getHelperText() { - return helperText; - } - - @Override - public void setError(CharSequence errorText) { - extendBottom(); - tempErrorText = errorText == null ? null : errorText.toString(); - postInvalidate(); - } - - @Override - public CharSequence getError() { - return tempErrorText; + // calculate the vertical position + int start = innerPaddingTop + floatingLabelTextSize + innerComponentsSpacing; + int distance = innerComponentsSpacing; + int position = (int) (start - distance * floatingLabelFraction); + + // calculate the alpha + int alpha = (int) (floatingLabelFraction * 0xff * (0.74f * focusFraction + 0.26f)); + paint.setAlpha(alpha); + + // draw the floating label + canvas.drawText(floatingLabelText.toString(), getPaddingLeft() + getScrollX(), position, paint); + } + + // draw the bottom ellipsis + if (hasFocus() && singleLineEllipsis && getScrollX() != 0) { + paint.setColor(primaryColor); + float startY = lineStartY + innerComponentsSpacing; + canvas.drawCircle(bottomEllipsisSize / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); + canvas.drawCircle(bottomEllipsisSize * 5 / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); + canvas.drawCircle(bottomEllipsisSize * 9 / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); } - /** - * if {@link #extendBottom} is false, set it true and reset the paddings. - */ - private void extendBottom() { - if (!extendBottom) { - extendBottom = true; - initPadding(); - } - } - - /** - * only used to draw the bottom line - */ - private boolean isInternalValid() { - return tempErrorText == null && isMaxCharactersValid(); - } - - /** - * if the main text matches the regex - */ - public boolean isValid(String regex) { - if (regex == null) { - return false; - } - Pattern pattern = Pattern.compile(regex); - Matcher matcher = pattern.matcher(getText()); - return matcher.matches(); - } - - /** - * check if the main text matches the regex, and set the error text if not. - * @return true if it matches the regex, false if not. - */ - public boolean validate(String regex, CharSequence errorText) { - boolean isValid = isValid(regex); - if (!isValid) { - setError(errorText); - } - postInvalidate(); - return isValid; - } - - @Override - public void setOnFocusChangeListener(OnFocusChangeListener listener) { - if (interFocusChangeListener == null) { - super.setOnFocusChangeListener(listener); - } else { - outerFocusChangeListener = listener; - } - } - - private ObjectAnimator getLabelAnimator() { - if (labelAnimator == null) { - labelAnimator = ObjectAnimator.ofFloat(this, "floatingLabelFraction", 0f, 1f); - } - return labelAnimator; - } - - private ObjectAnimator getLabelFocusAnimator() { - if (labelFocusAnimator == null) { - labelFocusAnimator = ObjectAnimator.ofFloat(this, "focusFraction", 0f, 1f); - } - return labelFocusAnimator; - } - - @Override - protected void onDraw(@NonNull Canvas canvas) { - // set the textSize - paint.setTextSize(floatingLabelTextSize); - - float lineStartY = getScrollY() + getHeight() - getPaddingBottom() + innerComponentsSpacing; - - // draw the background - if (!isInternalValid()) { // not valid - paint.setColor(errorColor); - canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(2), paint); - } else if (!isEnabled()) { // disabled - paint.setColor(baseColor & 0x00ffffff | 0x44000000); - float interval = getPixel(1); - for (float startX = 0; startX < getWidth(); startX += interval * 3) { - canvas.drawRect(getScrollX() + startX, lineStartY, getScrollX() + startX + interval, lineStartY + getPixel(1), paint); - } - } else if (hasFocus()) { // focused - paint.setColor(primaryColor); - canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(2), paint); - } else { // normal - paint.setColor(baseColor); - canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(1), paint); - } - - Paint.FontMetrics fontMetrics = paint.getFontMetrics(); - float relativeHeight = -fontMetrics.ascent - fontMetrics.descent; - - // draw the characters counter - if (maxCharacters > 0) { - paint.setColor(isMaxCharactersValid() ? getCurrentHintTextColor() : errorColor); - String text = getText().length() + " / " + maxCharacters; - canvas.drawText(text, getWidth() + getScrollX() - paint.measureText(text), lineStartY + innerComponentsSpacing + relativeHeight, paint); - } - - // draw the bottom text - float bottomTextStartX = getScrollX() + (singleLineEllipsis ? (bottomEllipsisSize * 5 + getPixel(4)) : 0); - if (tempErrorText != null) { // regex error - paint.setColor(errorColor); - canvas.drawText(tempErrorText, bottomTextStartX, lineStartY + innerComponentsSpacing + relativeHeight, paint); - } else if (!TextUtils.isEmpty(helperText)) { - paint.setColor(getCurrentHintTextColor()); - canvas.drawText(helperText, bottomTextStartX, lineStartY + innerComponentsSpacing + relativeHeight, paint); - } - - // draw the floating label - if (floatingLabelEnabled && !TextUtils.isEmpty(floatingLabelText)) { - // calculate the text color - paint.setColor((Integer) focusEvaluator.evaluate(focusFraction, getCurrentHintTextColor(), primaryColor)); - - // calculate the vertical position - int start = innerPaddingTop + floatingLabelTextSize + innerComponentsSpacing; - int distance = innerComponentsSpacing; - int position = (int) (start - distance * floatingLabelFraction); - - // calculate the alpha - int alpha = (int) (floatingLabelFraction * 0xff * (0.74f * focusFraction + 0.26f)); - paint.setAlpha(alpha); - - // draw the floating label - canvas.drawText(floatingLabelText.toString(), getPaddingLeft() + getScrollX(), position, paint); - } - - // draw the bottom ellipsis - if (hasFocus() && singleLineEllipsis && getScrollX() != 0) { - paint.setColor(primaryColor); - float startY = lineStartY + innerComponentsSpacing; - canvas.drawCircle(bottomEllipsisSize / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); - canvas.drawCircle(bottomEllipsisSize * 5 / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); - canvas.drawCircle(bottomEllipsisSize * 9 / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); - } - - // draw the original things - super.onDraw(canvas); - } - - public boolean isMaxCharactersValid() { - return maxCharacters <= 0 || getText() == null || getText().length() <= maxCharacters; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (singleLineEllipsis && getScrollX() > 0 && event.getAction() == MotionEvent.ACTION_DOWN && event.getX() < getPixel(4 * 5) && event.getY() > getHeight() - extraPaddingBottom - innerPaddingBottom && event.getY() < getHeight() - innerPaddingBottom) { - setSelection(0); - return false; - } - return super.onTouchEvent(event); - } + // draw the original things + super.onDraw(canvas); + } + + public boolean isMaxCharactersValid() { + return maxCharacters <= 0 || getText() == null || getText().length() <= maxCharacters; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (singleLineEllipsis && getScrollX() > 0 && event.getAction() == MotionEvent.ACTION_DOWN && event.getX() < getPixel(4 * 5) && event.getY() > getHeight() - extraPaddingBottom - innerPaddingBottom && event.getY() < getHeight() - innerPaddingBottom) { + setSelection(0); + return false; + } + return super.onTouchEvent(event); + } } diff --git a/library/src/main/java/com/rengwuxian/materialedittext/MaterialEditText.java b/library/src/main/java/com/rengwuxian/materialedittext/MaterialEditText.java index d1073054..6edb2a99 100644 --- a/library/src/main/java/com/rengwuxian/materialedittext/MaterialEditText.java +++ b/library/src/main/java/com/rengwuxian/materialedittext/MaterialEditText.java @@ -32,601 +32,602 @@ *

*/ public class MaterialEditText extends EditText { - public static final int FLOATING_LABEL_NONE = 0; - public static final int FLOATING_LABEL_NORMAL = 1; - public static final int FLOATING_LABEL_HIGHLIGHT = 2; - - /** - * the spacing between the main text and the inner top padding. - */ - private int extraPaddingTop; - - /** - * the spacing between the main text and the inner bottom padding. - */ - private int extraPaddingBottom; - - /** - * the floating label's text size. - */ - private final int floatingLabelTextSize; - - /** - * the spacing between the main text and the inner components (floating label, bottom ellipsis, characters counter). - */ - private final int innerComponentsSpacing; - - /** - * whether the floating label should be shown. default is false. - */ - private boolean floatingLabelEnabled; - - /** - * whether to highlight the floating label's text color when focused (with the main color). default is true. - */ - private boolean highlightFloatingLabel; - - /** - * the base color of the line and the texts. default is black. - */ - private int baseColor; - - /** - * inner top padding - */ - private int innerPaddingTop; - - /** - * inner bottom padding - */ - private int innerPaddingBottom; - - /** - * the underline's highlight color, and the highlight color of the floating label if app:highlightFloatingLabel is set true in the xml. default is black(when app:darkTheme is false) or white(when app:darkTheme is true) - */ - private int primaryColor; - - /** - * the color for when something is wrong.(e.g. exceeding max characters) - */ - private int errorColor; - - /** - * characters count limit. 0 means no limit. default is 0. NOTE: the character counter will increase the View's height. - */ - private int maxCharacters; - - /** - * whether to show the bottom ellipsis in singleLine mode. default is false. NOTE: the bottom ellipsis will increase the View's height. - */ - private boolean singleLineEllipsis; - - /** - * bottom ellipsis's height - */ - private final int bottomEllipsisSize; - - /** - * If the MaterialEditText always extends its bottom space. If it's true, the MaterialEditText always extends some space at the bottom; and if it's false, the MaterialEditText only extends some space when needed (e.g. when an error text is being shown, and there's no extra space at the bottom). - */ - private boolean extendBottom; - - /** - * Helper text at the bottom - */ - private String helperText; - - /** - * error text for manually invoked {@link #setError(CharSequence)} - */ - private String tempErrorText; - - /** - * animation fraction of the floating label (0 as totally hidden). - */ - private float floatingLabelFraction; - - /** - * whether the floating label is being shown. - */ - private boolean floatingLabelShown; - - /** - * the floating label's focusFraction - */ - private float focusFraction; - - /** - * The font used for the accent texts (floating label, error/helper text, character counter, etc.) - */ - private Typeface accentTypeface; - - /** - * Text for the floatLabel if different from the hint - */ - private CharSequence floatingLabelText; - - private ArgbEvaluator focusEvaluator = new ArgbEvaluator(); - Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - ObjectAnimator labelAnimator; - ObjectAnimator labelFocusAnimator; - OnFocusChangeListener interFocusChangeListener; - OnFocusChangeListener outerFocusChangeListener; - - public MaterialEditText(Context context) { - this(context, null); - } - - public MaterialEditText(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public MaterialEditText(Context context, AttributeSet attrs, int style) { - super(context, attrs, style); - - setFocusable(true); - setFocusableInTouchMode(true); - setClickable(true); - - floatingLabelTextSize = getResources().getDimensionPixelSize(R.dimen.floating_label_text_size); - innerComponentsSpacing = getResources().getDimensionPixelSize(R.dimen.inner_components_spacing); - bottomEllipsisSize = getResources().getDimensionPixelSize(R.dimen.bottom_ellipsis_height); - - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialEditText); - baseColor = typedArray.getColor(R.styleable.MaterialEditText_baseColor, Color.BLACK); - ColorStateList colorStateList = new ColorStateList(new int[][] {new int[] {android.R.attr.state_enabled}, EMPTY_STATE_SET}, new int[] {baseColor & 0x00ffffff | 0xdf000000, baseColor & 0x00ffffff | 0x44000000}); - setTextColor(colorStateList); - - primaryColor = typedArray.getColor(R.styleable.MaterialEditText_primaryColor, baseColor); - setFloatingLabelInternal(typedArray.getInt(R.styleable.MaterialEditText_floatingLabel, 0)); - errorColor = typedArray.getColor(R.styleable.MaterialEditText_errorColor, Color.parseColor("#e7492E")); - maxCharacters = typedArray.getInt(R.styleable.MaterialEditText_maxCharacters, 0); - singleLineEllipsis = typedArray.getBoolean(R.styleable.MaterialEditText_singleLineEllipsis, false); - helperText = typedArray.getString(R.styleable.MaterialEditText_helperText); - extendBottom = typedArray.getBoolean(R.styleable.MaterialEditText_extendBottom, false) || helperText != null || maxCharacters > 0 || singleLineEllipsis; - String fontPath = typedArray.getString(R.styleable.MaterialEditText_accentTypeface); - if (fontPath != null) { - accentTypeface = getCustomTypeface(fontPath); - paint.setTypeface(accentTypeface); + public static final int FLOATING_LABEL_NONE = 0; + public static final int FLOATING_LABEL_NORMAL = 1; + public static final int FLOATING_LABEL_HIGHLIGHT = 2; + + /** + * the spacing between the main text and the inner top padding. + */ + private int extraPaddingTop; + + /** + * the spacing between the main text and the inner bottom padding. + */ + private int extraPaddingBottom; + + /** + * the floating label's text size. + */ + private final int floatingLabelTextSize; + + /** + * the spacing between the main text and the inner components (floating label, bottom ellipsis, characters counter). + */ + private final int innerComponentsSpacing; + + /** + * whether the floating label should be shown. default is false. + */ + private boolean floatingLabelEnabled; + + /** + * whether to highlight the floating label's text color when focused (with the main color). default is true. + */ + private boolean highlightFloatingLabel; + + /** + * the base color of the line and the texts. default is black. + */ + private int baseColor; + + /** + * inner top padding + */ + private int innerPaddingTop; + + /** + * inner bottom padding + */ + private int innerPaddingBottom; + + /** + * the underline's highlight color, and the highlight color of the floating label if app:highlightFloatingLabel is set true in the xml. default is black(when app:darkTheme is false) or white(when app:darkTheme is true) + */ + private int primaryColor; + + /** + * the color for when something is wrong.(e.g. exceeding max characters) + */ + private int errorColor; + + /** + * characters count limit. 0 means no limit. default is 0. NOTE: the character counter will increase the View's height. + */ + private int maxCharacters; + + /** + * whether to show the bottom ellipsis in singleLine mode. default is false. NOTE: the bottom ellipsis will increase the View's height. + */ + private boolean singleLineEllipsis; + + /** + * bottom ellipsis's height + */ + private final int bottomEllipsisSize; + + /** + * If the MaterialEditText always extends its bottom space. If it's true, the MaterialEditText always extends some space at the bottom; and if it's false, the MaterialEditText only extends some space when needed (e.g. when an error text is being shown, and there's no extra space at the bottom). + */ + private boolean extendBottom; + + /** + * Helper text at the bottom + */ + private String helperText; + + /** + * error text for manually invoked {@link #setError(CharSequence)} + */ + private String tempErrorText; + + /** + * animation fraction of the floating label (0 as totally hidden). + */ + private float floatingLabelFraction; + + /** + * whether the floating label is being shown. + */ + private boolean floatingLabelShown; + + /** + * the floating label's focusFraction + */ + private float focusFraction; + + /** + * The font used for the accent texts (floating label, error/helper text, character counter, etc.) + */ + private Typeface accentTypeface; + + /** + * Text for the floatLabel if different from the hint + */ + private CharSequence floatingLabelText; + + private ArgbEvaluator focusEvaluator = new ArgbEvaluator(); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + ObjectAnimator labelAnimator; + ObjectAnimator labelFocusAnimator; + OnFocusChangeListener interFocusChangeListener; + OnFocusChangeListener outerFocusChangeListener; + + public MaterialEditText(Context context) { + this(context, null); + } + + public MaterialEditText(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public MaterialEditText(Context context, AttributeSet attrs, int style) { + super(context, attrs, style); + + setFocusable(true); + setFocusableInTouchMode(true); + setClickable(true); + + floatingLabelTextSize = getResources().getDimensionPixelSize(R.dimen.floating_label_text_size); + innerComponentsSpacing = getResources().getDimensionPixelSize(R.dimen.inner_components_spacing); + bottomEllipsisSize = getResources().getDimensionPixelSize(R.dimen.bottom_ellipsis_height); + + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialEditText); + baseColor = typedArray.getColor(R.styleable.MaterialEditText_baseColor, Color.BLACK); + ColorStateList colorStateList = new ColorStateList(new int[][]{new int[]{android.R.attr.state_enabled}, EMPTY_STATE_SET}, new int[]{baseColor & 0x00ffffff | 0xdf000000, baseColor & 0x00ffffff | 0x44000000}); + setTextColor(colorStateList); + + primaryColor = typedArray.getColor(R.styleable.MaterialEditText_primaryColor, baseColor); + setFloatingLabelInternal(typedArray.getInt(R.styleable.MaterialEditText_floatingLabel, 0)); + errorColor = typedArray.getColor(R.styleable.MaterialEditText_errorColor, Color.parseColor("#e7492E")); + maxCharacters = typedArray.getInt(R.styleable.MaterialEditText_maxCharacters, 0); + singleLineEllipsis = typedArray.getBoolean(R.styleable.MaterialEditText_singleLineEllipsis, false); + helperText = typedArray.getString(R.styleable.MaterialEditText_helperText); + extendBottom = typedArray.getBoolean(R.styleable.MaterialEditText_extendBottom, false) || helperText != null || maxCharacters > 0 || singleLineEllipsis; + String fontPath = typedArray.getString(R.styleable.MaterialEditText_accentTypeface); + if (fontPath != null) { + accentTypeface = getCustomTypeface(fontPath); + paint.setTypeface(accentTypeface); + } + floatingLabelText = typedArray.getString(R.styleable.MaterialEditText_floatingLabelText); + if (floatingLabelText == null) { + floatingLabelText = getHint(); + } + typedArray.recycle(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + setBackground(null); + } else { + setBackgroundDrawable(null); + } + if (singleLineEllipsis) { + TransformationMethod transformationMethod = getTransformationMethod(); + setSingleLine(); + setTransformationMethod(transformationMethod); + } + initPadding(); + initText(); + initFloatingLabel(); + initErrorTextListener(); + } + + private void initText() { + if (!TextUtils.isEmpty(getText())) { + CharSequence text = getText(); + setText(null); + setHintTextColor(baseColor & 0x00ffffff | 0x44000000); + setText(text); + floatingLabelFraction = 1; + floatingLabelShown = true; + } else { + setHintTextColor(baseColor & 0x00ffffff | 0x44000000); + } + } + + private void initErrorTextListener() { + 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) { + tempErrorText = null; + invalidate(); + } + }); + } + + private Typeface getCustomTypeface(@NonNull String fontPath) { + return Typeface.createFromAsset(getContext().getAssets(), fontPath); + } + + public float getFloatingLabelFraction() { + return floatingLabelFraction; + } + + public void setFloatingLabelFraction(float floatingLabelFraction) { + this.floatingLabelFraction = floatingLabelFraction; + invalidate(); + } + + public float getFocusFraction() { + return focusFraction; + } + + public void setFocusFraction(float focusFraction) { + this.focusFraction = focusFraction; + invalidate(); + } + + @Nullable + public Typeface getAccentTypeface() { + return accentTypeface; + } + + /** + * Set typeface used for the accent texts (floating label, error/helper text, character counter, etc.) + */ + public void setAccentTypeface(Typeface accentTypeface) { + this.accentTypeface = accentTypeface; + this.paint.setTypeface(accentTypeface); + postInvalidate(); + } + + public CharSequence getFloatingLabelText() { + return floatingLabelText; + } + + /** + * Set the floating label text. + *

+ * Pass null to force fallback to use hint's value. + * + * @param floatingLabelText + */ + public void setFloatingLabelText(@Nullable CharSequence floatingLabelText) { + this.floatingLabelText = floatingLabelText == null ? getHint() : floatingLabelText; + postInvalidate(); + } + + private int getPixel(int dp) { + return Density.dp2px(getContext(), dp); + } + + private void initPadding() { + int paddingTop = getPaddingTop() - extraPaddingTop; + int paddingBottom = getPaddingBottom() - extraPaddingBottom; + extraPaddingTop = floatingLabelEnabled ? floatingLabelTextSize + innerComponentsSpacing : innerComponentsSpacing; + extraPaddingBottom = extendBottom ? floatingLabelTextSize : 0; + extraPaddingBottom += innerComponentsSpacing * 2; + setPaddings(getPaddingLeft(), paddingTop, getPaddingRight(), paddingBottom); + } + + /** + * use {@link #setPaddings(int, int, int, int)} instead, or the paddingTop and the paddingBottom may be set incorrectly. + */ + @Deprecated + @Override + public final void setPadding(int left, int top, int right, int bottom) { + super.setPadding(left, top, right, bottom); + } + + /** + * Use this method instead of {@link #setPadding(int, int, int, int)} to automatically set the paddingTop and the paddingBottom correctly. + */ + public void setPaddings(int left, int top, int right, int bottom) { + innerPaddingTop = top; + innerPaddingBottom = bottom; + super.setPadding(left, top + extraPaddingTop, right, bottom + extraPaddingBottom); + } + + /** + * get inner top padding, not the real paddingTop + */ + public int getInnerPaddingTop() { + return innerPaddingTop; + } + + /** + * get inner bottom padding, not the real paddingBottom + */ + public int getInnerPaddingBottom() { + return innerPaddingBottom; + } + + private void initFloatingLabel() { + if (floatingLabelEnabled) { + // observe the text changing + 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) { } - floatingLabelText = typedArray.getString(R.styleable.MaterialEditText_floatingLabelText); - if (floatingLabelText == null) { - floatingLabelText = getHint(); + + @Override + public void afterTextChanged(Editable s) { + if (s.length() == 0) { + if (floatingLabelShown) { + floatingLabelShown = false; + getLabelAnimator().reverse(); + } + } else if (!floatingLabelShown) { + floatingLabelShown = true; + if (getLabelAnimator().isStarted()) { + getLabelAnimator().reverse(); + } else { + getLabelAnimator().start(); + } + } } - typedArray.recycle(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - setBackground(null); - } else { - setBackgroundDrawable(null); - } - if (singleLineEllipsis) { - TransformationMethod transformationMethod = getTransformationMethod(); - setSingleLine(); - setTransformationMethod(transformationMethod); - } - initPadding(); - initText(); - initFloatingLabel(); - initErrorTextListener(); - } - - private void initText() { - if (!TextUtils.isEmpty(getText())) { - CharSequence text = getText(); - setText(null); - setHintTextColor(baseColor & 0x00ffffff | 0x44000000); - setText(text); - floatingLabelFraction = 1; - floatingLabelShown = true; - } else { - setHintTextColor(baseColor & 0x00ffffff | 0x44000000); - } - } - - private void initErrorTextListener() { - 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) { - tempErrorText = null; - invalidate(); - } - }); - } - - private Typeface getCustomTypeface(@NonNull String fontPath) { - return Typeface.createFromAsset(getContext().getAssets(), fontPath); + }); + if (highlightFloatingLabel) { + // observe the focus state to animate the floating label's text color appropriately + interFocusChangeListener = new OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + if (getLabelFocusAnimator().isStarted()) { + getLabelFocusAnimator().reverse(); + } else { + getLabelFocusAnimator().start(); + } + } else { + getLabelFocusAnimator().reverse(); + } + if (outerFocusChangeListener != null) { + outerFocusChangeListener.onFocusChange(v, hasFocus); + } + } + }; + super.setOnFocusChangeListener(interFocusChangeListener); + } + } + + } + + public void setBaseColor(int color) { + baseColor = color; + postInvalidate(); + } + + public void setPrimaryColor(int color) { + primaryColor = color; + postInvalidate(); + } + + private void setFloatingLabelInternal(int mode) { + switch (mode) { + case FLOATING_LABEL_NORMAL: + floatingLabelEnabled = true; + highlightFloatingLabel = false; + break; + case FLOATING_LABEL_HIGHLIGHT: + floatingLabelEnabled = true; + highlightFloatingLabel = true; + break; + default: + floatingLabelEnabled = false; + highlightFloatingLabel = false; + break; } + } - public float getFloatingLabelFraction() { - return floatingLabelFraction; - } + public void setFloatingLabel(int mode) { + setFloatingLabelInternal(mode); + postInvalidate(); + } + + public void setSingleLineEllipsis() { + setSingleLineEllipsis(true); + } + + public void setSingleLineEllipsis(boolean enabled) { + singleLineEllipsis = enabled; + if (enabled) { + extendBottom(); + } + postInvalidate(); + } - public void setFloatingLabelFraction(float floatingLabelFraction) { - this.floatingLabelFraction = floatingLabelFraction; - invalidate(); - } + public int getMaxCharacters() { + return maxCharacters; + } - public float getFocusFraction() { - return focusFraction; - } + public void setMaxCharacters(int max) { + maxCharacters = max; + if (max > 0) { + extendBottom(); + } + postInvalidate(); + } + + public void setErrorColor(int color) { + errorColor = color; + postInvalidate(); + } + + public void setHelperText(CharSequence helperText) { + this.helperText = helperText == null ? null : helperText.toString(); + extendBottom(); + postInvalidate(); + } + + public String getHelperText() { + return helperText; + } + + @Override + public void setError(CharSequence errorText) { + extendBottom(); + tempErrorText = errorText == null ? null : errorText.toString(); + postInvalidate(); + } + + @Override + public CharSequence getError() { + return tempErrorText; + } + + /** + * if {@link #extendBottom} is false, set it true and reset the paddings. + */ + private void extendBottom() { + if (!extendBottom) { + extendBottom = true; + initPadding(); + } + } + + /** + * only used to draw the bottom line + */ + private boolean isInternalValid() { + return tempErrorText == null && isMaxCharactersValid(); + } + + /** + * if the main text matches the regex + */ + public boolean isValid(String regex) { + if (regex == null) { + return false; + } + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(getText()); + return matcher.matches(); + } + + /** + * check if the main text matches the regex, and set the error text if not. + * + * @return true if it matches the regex, false if not. + */ + public boolean validate(String regex, CharSequence errorText) { + boolean isValid = isValid(regex); + if (!isValid) { + setError(errorText); + } + postInvalidate(); + return isValid; + } + + @Override + public void setOnFocusChangeListener(OnFocusChangeListener listener) { + if (interFocusChangeListener == null) { + super.setOnFocusChangeListener(listener); + } else { + outerFocusChangeListener = listener; + } + } - public void setFocusFraction(float focusFraction) { - this.focusFraction = focusFraction; - invalidate(); - } + private ObjectAnimator getLabelAnimator() { + if (labelAnimator == null) { + labelAnimator = ObjectAnimator.ofFloat(this, "floatingLabelFraction", 0f, 1f); + } + return labelAnimator; + } - @Nullable - public Typeface getAccentTypeface() { - return accentTypeface; + private ObjectAnimator getLabelFocusAnimator() { + if (labelFocusAnimator == null) { + labelFocusAnimator = ObjectAnimator.ofFloat(this, "focusFraction", 0f, 1f); + } + return labelFocusAnimator; + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + // set the textSize + paint.setTextSize(floatingLabelTextSize); + + float lineStartY = getScrollY() + getHeight() - getPaddingBottom() + innerComponentsSpacing; + + // draw the background + if (!isInternalValid()) { // not valid + paint.setColor(errorColor); + canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(2), paint); + } else if (!isEnabled()) { // disabled + paint.setColor(baseColor & 0x00ffffff | 0x44000000); + float interval = getPixel(1); + for (float startX = 0; startX < getWidth(); startX += interval * 3) { + canvas.drawRect(getScrollX() + startX, lineStartY, getScrollX() + startX + interval, lineStartY + getPixel(1), paint); + } + } else if (hasFocus()) { // focused + paint.setColor(primaryColor); + canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(2), paint); + } else { // normal + paint.setColor(baseColor); + canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(1), paint); } - /** - * Set typeface used for the accent texts (floating label, error/helper text, character counter, etc.) - */ - public void setAccentTypeface(Typeface accentTypeface) { - this.accentTypeface = accentTypeface; - this.paint.setTypeface(accentTypeface); - postInvalidate(); + Paint.FontMetrics fontMetrics = paint.getFontMetrics(); + float relativeHeight = -fontMetrics.ascent - fontMetrics.descent; + + // draw the characters counter + if (maxCharacters > 0) { + paint.setColor(isMaxCharactersValid() ? getCurrentHintTextColor() : errorColor); + String text = getText().length() + " / " + maxCharacters; + canvas.drawText(text, getWidth() + getScrollX() - paint.measureText(text), lineStartY + innerComponentsSpacing + relativeHeight, paint); } - public CharSequence getFloatingLabelText() { - return floatingLabelText; + // draw the bottom text + float bottomTextStartX = getScrollX() + (singleLineEllipsis ? (bottomEllipsisSize * 5 + getPixel(4)) : 0); + if (tempErrorText != null) { // regex error + paint.setColor(errorColor); + canvas.drawText(tempErrorText, bottomTextStartX, lineStartY + innerComponentsSpacing + relativeHeight, paint); + } else if (!TextUtils.isEmpty(helperText)) { + paint.setColor(getCurrentHintTextColor()); + canvas.drawText(helperText, bottomTextStartX, lineStartY + innerComponentsSpacing + relativeHeight, paint); } - /** - * Set the floating label text. - * - * Pass null to force fallback to use hint's value. - * - * @param floatingLabelText - */ - public void setFloatingLabelText(@Nullable CharSequence floatingLabelText) { - this.floatingLabelText = floatingLabelText == null ? getHint() : floatingLabelText; - postInvalidate(); + // draw the floating label + if (floatingLabelEnabled && !TextUtils.isEmpty(floatingLabelText)) { + // calculate the text color + paint.setColor((Integer) focusEvaluator.evaluate(focusFraction, getCurrentHintTextColor(), primaryColor)); + + // calculate the vertical position + int start = innerPaddingTop + floatingLabelTextSize + innerComponentsSpacing; + int distance = innerComponentsSpacing; + int position = (int) (start - distance * floatingLabelFraction); + + // calculate the alpha + int alpha = (int) (floatingLabelFraction * 0xff * (0.74f * focusFraction + 0.26f)); + paint.setAlpha(alpha); + + // draw the floating label + canvas.drawText(floatingLabelText.toString(), getPaddingLeft() + getScrollX(), position, paint); } - private int getPixel(int dp) { - return Density.dp2px(getContext(), dp); - } - - private void initPadding() { - int paddingTop = getPaddingTop() - extraPaddingTop; - int paddingBottom = getPaddingBottom() - extraPaddingBottom; - extraPaddingTop = floatingLabelEnabled ? floatingLabelTextSize + innerComponentsSpacing : innerComponentsSpacing; - extraPaddingBottom = extendBottom ? floatingLabelTextSize : 0; - extraPaddingBottom += innerComponentsSpacing * 2; - setPaddings(getPaddingLeft(), paddingTop, getPaddingRight(), paddingBottom); - } - - /** - * use {@link #setPaddings(int, int, int, int)} instead, or the paddingTop and the paddingBottom may be set incorrectly. - */ - @Deprecated - @Override - public final void setPadding(int left, int top, int right, int bottom) { - super.setPadding(left, top, right, bottom); - } - - /** - * Use this method instead of {@link #setPadding(int, int, int, int)} to automatically set the paddingTop and the paddingBottom correctly. - */ - public void setPaddings(int left, int top, int right, int bottom) { - innerPaddingTop = top; - innerPaddingBottom = bottom; - super.setPadding(left, top + extraPaddingTop, right, bottom + extraPaddingBottom); - } - - /** - * get inner top padding, not the real paddingTop - */ - public int getInnerPaddingTop() { - return innerPaddingTop; - } - - /** - * get inner bottom padding, not the real paddingBottom - */ - public int getInnerPaddingBottom() { - return innerPaddingBottom; - } - - private void initFloatingLabel() { - if (floatingLabelEnabled) { - // observe the text changing - 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.length() == 0) { - if (floatingLabelShown) { - floatingLabelShown = false; - getLabelAnimator().reverse(); - } - } else if (!floatingLabelShown) { - floatingLabelShown = true; - if (getLabelAnimator().isStarted()) { - getLabelAnimator().reverse(); - } else { - getLabelAnimator().start(); - } - } - } - }); - if (highlightFloatingLabel) { - // observe the focus state to animate the floating label's text color appropriately - interFocusChangeListener = new OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) { - if (getLabelFocusAnimator().isStarted()) { - getLabelFocusAnimator().reverse(); - } else { - getLabelFocusAnimator().start(); - } - } else { - getLabelFocusAnimator().reverse(); - } - if (outerFocusChangeListener != null) { - outerFocusChangeListener.onFocusChange(v, hasFocus); - } - } - }; - super.setOnFocusChangeListener(interFocusChangeListener); - } - } - - } - - public void setBaseColor(int color) { - baseColor = color; - postInvalidate(); - } - - public void setPrimaryColor(int color) { - primaryColor = color; - postInvalidate(); - } - - private void setFloatingLabelInternal(int mode) { - switch (mode) { - case FLOATING_LABEL_NORMAL: - floatingLabelEnabled = true; - highlightFloatingLabel = false; - break; - case FLOATING_LABEL_HIGHLIGHT: - floatingLabelEnabled = true; - highlightFloatingLabel = true; - break; - default: - floatingLabelEnabled = false; - highlightFloatingLabel = false; - break; - } - } - - public void setFloatingLabel(int mode) { - setFloatingLabelInternal(mode); - postInvalidate(); - } - - public void setSingleLineEllipsis() { - setSingleLineEllipsis(true); - } - - public void setSingleLineEllipsis(boolean enabled) { - singleLineEllipsis = enabled; - if (enabled) { - extendBottom(); - } - postInvalidate(); - } - - public int getMaxCharacters() { - return maxCharacters; - } - - public void setMaxCharacters(int max) { - maxCharacters = max; - if (max > 0) { - extendBottom(); - } - postInvalidate(); - } - - public void setErrorColor(int color) { - errorColor = color; - postInvalidate(); - } - - public void setHelperText(CharSequence helperText) { - this.helperText = helperText == null ? null : helperText.toString(); - extendBottom(); - postInvalidate(); - } - - public String getHelperText() { - return helperText; - } - - @Override - public void setError(CharSequence errorText) { - extendBottom(); - tempErrorText = errorText == null ? null : errorText.toString(); - postInvalidate(); - } - - @Override - public CharSequence getError() { - return tempErrorText; + // draw the bottom ellipsis + if (hasFocus() && singleLineEllipsis && getScrollX() != 0) { + paint.setColor(primaryColor); + float startY = lineStartY + innerComponentsSpacing; + canvas.drawCircle(bottomEllipsisSize / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); + canvas.drawCircle(bottomEllipsisSize * 5 / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); + canvas.drawCircle(bottomEllipsisSize * 9 / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); } - /** - * if {@link #extendBottom} is false, set it true and reset the paddings. - */ - private void extendBottom() { - if (!extendBottom) { - extendBottom = true; - initPadding(); - } - } - - /** - * only used to draw the bottom line - */ - private boolean isInternalValid() { - return tempErrorText == null && isMaxCharactersValid(); - } - - /** - * if the main text matches the regex - */ - public boolean isValid(String regex) { - if (regex == null) { - return false; - } - Pattern pattern = Pattern.compile(regex); - Matcher matcher = pattern.matcher(getText()); - return matcher.matches(); - } - - /** - * check if the main text matches the regex, and set the error text if not. - * @return true if it matches the regex, false if not. - */ - public boolean validate(String regex, CharSequence errorText) { - boolean isValid = isValid(regex); - if (!isValid) { - setError(errorText); - } - postInvalidate(); - return isValid; - } - - @Override - public void setOnFocusChangeListener(OnFocusChangeListener listener) { - if (interFocusChangeListener == null) { - super.setOnFocusChangeListener(listener); - } else { - outerFocusChangeListener = listener; - } - } - - private ObjectAnimator getLabelAnimator() { - if (labelAnimator == null) { - labelAnimator = ObjectAnimator.ofFloat(this, "floatingLabelFraction", 0f, 1f); - } - return labelAnimator; - } - - private ObjectAnimator getLabelFocusAnimator() { - if (labelFocusAnimator == null) { - labelFocusAnimator = ObjectAnimator.ofFloat(this, "focusFraction", 0f, 1f); - } - return labelFocusAnimator; - } - - @Override - protected void onDraw(@NonNull Canvas canvas) { - // set the textSize - paint.setTextSize(floatingLabelTextSize); - - float lineStartY = getScrollY() + getHeight() - getPaddingBottom() + innerComponentsSpacing; - - // draw the background - if (!isInternalValid()) { // not valid - paint.setColor(errorColor); - canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(2), paint); - } else if (!isEnabled()) { // disabled - paint.setColor(baseColor & 0x00ffffff | 0x44000000); - float interval = getPixel(1); - for (float startX = 0; startX < getWidth(); startX += interval * 3) { - canvas.drawRect(getScrollX() + startX, lineStartY, getScrollX() + startX + interval, lineStartY + getPixel(1), paint); - } - } else if (hasFocus()) { // focused - paint.setColor(primaryColor); - canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(2), paint); - } else { // normal - paint.setColor(baseColor); - canvas.drawRect(getScrollX(), lineStartY, getWidth() + getScrollX(), lineStartY + getPixel(1), paint); - } - - Paint.FontMetrics fontMetrics = paint.getFontMetrics(); - float relativeHeight = -fontMetrics.ascent - fontMetrics.descent; - - // draw the characters counter - if (maxCharacters > 0) { - paint.setColor(isMaxCharactersValid() ? getCurrentHintTextColor() : errorColor); - String text = getText().length() + " / " + maxCharacters; - canvas.drawText(text, getWidth() + getScrollX() - paint.measureText(text), lineStartY + innerComponentsSpacing + relativeHeight, paint); - } - - // draw the bottom text - float bottomTextStartX = getScrollX() + (singleLineEllipsis ? (bottomEllipsisSize * 5 + getPixel(4)) : 0); - if (tempErrorText != null) { // regex error - paint.setColor(errorColor); - canvas.drawText(tempErrorText, bottomTextStartX, lineStartY + innerComponentsSpacing + relativeHeight, paint); - } else if (!TextUtils.isEmpty(helperText)) { - paint.setColor(getCurrentHintTextColor()); - canvas.drawText(helperText, bottomTextStartX, lineStartY + innerComponentsSpacing + relativeHeight, paint); - } - - // draw the floating label - if (floatingLabelEnabled && !TextUtils.isEmpty(floatingLabelText)) { - // calculate the text color - paint.setColor((Integer) focusEvaluator.evaluate(focusFraction, getCurrentHintTextColor(), primaryColor)); - - // calculate the vertical position - int start = innerPaddingTop + floatingLabelTextSize + innerComponentsSpacing; - int distance = innerComponentsSpacing; - int position = (int) (start - distance * floatingLabelFraction); - - // calculate the alpha - int alpha = (int) (floatingLabelFraction * 0xff * (0.74f * focusFraction + 0.26f)); - paint.setAlpha(alpha); - - // draw the floating label - canvas.drawText(floatingLabelText.toString(), getPaddingLeft() + getScrollX(), position, paint); - } - - // draw the bottom ellipsis - if (hasFocus() && singleLineEllipsis && getScrollX() != 0) { - paint.setColor(primaryColor); - float startY = lineStartY + innerComponentsSpacing; - canvas.drawCircle(bottomEllipsisSize / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); - canvas.drawCircle(bottomEllipsisSize * 5 / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); - canvas.drawCircle(bottomEllipsisSize * 9 / 2 + getScrollX(), startY + bottomEllipsisSize / 2, bottomEllipsisSize / 2, paint); - } - - // draw the original things - super.onDraw(canvas); - } - - public boolean isMaxCharactersValid() { - return maxCharacters <= 0 || getText() == null || getText().length() <= maxCharacters; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (singleLineEllipsis && getScrollX() > 0 && event.getAction() == MotionEvent.ACTION_DOWN && event.getX() < getPixel(4 * 5) && event.getY() > getHeight() - extraPaddingBottom - innerPaddingBottom && event.getY() < getHeight() - innerPaddingBottom) { - setSelection(0); - return false; - } - return super.onTouchEvent(event); - } + // draw the original things + super.onDraw(canvas); + } + + public boolean isMaxCharactersValid() { + return maxCharacters <= 0 || getText() == null || getText().length() <= maxCharacters; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (singleLineEllipsis && getScrollX() > 0 && event.getAction() == MotionEvent.ACTION_DOWN && event.getX() < getPixel(4 * 5) && event.getY() > getHeight() - extraPaddingBottom - innerPaddingBottom && event.getY() < getHeight() - innerPaddingBottom) { + setSelection(0); + return false; + } + return super.onTouchEvent(event); + } } \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 0930c2be..13d24d15 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -20,6 +20,6 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:21.0.2' - compile 'com.rengwuxian.materialedittext:library:1.4.3' -// compile project(':library') +// compile 'com.rengwuxian.materialedittext:library:1.4.3' + compile project(':library') } diff --git a/sample/sample.iml b/sample/sample.iml index 81f95f0b..8e041d97 100644 --- a/sample/sample.iml +++ b/sample/sample.iml @@ -85,9 +85,9 @@ - + diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml index 30b9163e..ee4a03d9 100644 --- a/sample/src/main/res/layout/activity_main.xml +++ b/sample/src/main/res/layout/activity_main.xml @@ -1,390 +1,411 @@ - - - - - - - - - - - - - - - - - - - - -