From 115715f2f53a26d6df0b5156460a028eb89c95e2 Mon Sep 17 00:00:00 2001 From: Ztiany Date: Fri, 6 Dec 2019 12:47:05 +0800 Subject: [PATCH] add ClearableEditText, add new reg --- .../com/android/base/utils/common/Strings.kt | 19 +- .../base/widget/text/ClearableEditText.java | 290 ++++++++++++++++++ .../res/drawable-xxhdpi/base_icon_clear.png | Bin 0 -> 506 bytes .../base_icon_password_close.png | Bin 0 -> 1613 bytes .../base_icon_password_open.png | Bin 0 -> 2418 bytes lib_base/src/main/res/values/base_attrs.xml | 128 ++++---- 6 files changed, 372 insertions(+), 65 deletions(-) create mode 100644 lib_base/src/main/java/com/android/base/widget/text/ClearableEditText.java create mode 100644 lib_base/src/main/res/drawable-xxhdpi/base_icon_clear.png create mode 100644 lib_base/src/main/res/drawable-xxhdpi/base_icon_password_close.png create mode 100644 lib_base/src/main/res/drawable-xxhdpi/base_icon_password_open.png diff --git a/lib_base/src/main/java/com/android/base/utils/common/Strings.kt b/lib_base/src/main/java/com/android/base/utils/common/Strings.kt index bf5ca5f..2a8c14c 100644 --- a/lib_base/src/main/java/com/android/base/utils/common/Strings.kt +++ b/lib_base/src/main/java/com/android/base/utils/common/Strings.kt @@ -2,10 +2,11 @@ package com.android.base.utils.common + import java.util.regex.Pattern private const val CHINA_PHONE_REG = "^1\\d{10}$" -private const val ID_CARD_REG = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}" +private const val ID_CARD_REG = "(^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)" private const val EMAIL_REG = "\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*" private const val DIGIT_REG = "-?[1-9]\\d+\"" private const val URL_REG = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?" @@ -16,6 +17,8 @@ private const val CONTAINS_DIGITAL_REG = ".*[0-9]+.*" private const val CONTAINS_LETTERS_REG = ".*[a-zA-Z]+.*" private const val CONTAINS_LOWERCASE_LETTERS_REG = "^.*[a-z]+.*$" private const val CONTAINS_UPPERCASE_LETTERS_REG = "^.*[A-Z]+.*$" +private const val CHINESE_HAN_NATIONALITY_NAME_REG = "^[\\u4E00-\\u9FA5]{2,4}\$" +private const val CHINESE_NAME_REG = "^[\\u4E00-\\u9FA5]+(·[\\u4E00-\\u9FA5]+)*\$" /** * 验证中国的手机号 @@ -24,6 +27,20 @@ fun isChinaPhoneNumber(mobile: String?): Boolean { return !isEmpty(mobile) && Pattern.matches(CHINA_PHONE_REG, mobile) } +/** + * 验证中文姓名 + */ +fun isChineseName(name: String?): Boolean { + return !isEmpty(name) && Pattern.matches(CHINESE_NAME_REG, name) +} + +/** + * 验证中文汉族姓名 + */ +fun isChineseHanNationalityName(name: String?): Boolean { + return !isEmpty(name) && Pattern.matches(CHINESE_HAN_NATIONALITY_NAME_REG, name) +} + /** * 是否只包含 [1-9][a-z][A-Z] */ diff --git a/lib_base/src/main/java/com/android/base/widget/text/ClearableEditText.java b/lib_base/src/main/java/com/android/base/widget/text/ClearableEditText.java new file mode 100644 index 0000000..156c5a1 --- /dev/null +++ b/lib_base/src/main/java/com/android/base/widget/text/ClearableEditText.java @@ -0,0 +1,290 @@ +package com.android.base.widget.text; + + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.BitmapDrawable; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.PasswordTransformationMethod; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.inputmethod.EditorInfo; + +import com.android.base.R; +import com.android.base.utils.android.views.Sizes; + +import org.jetbrains.annotations.NotNull; + +import androidx.appcompat.widget.AppCompatEditText; +import timber.log.Timber; + + +public class ClearableEditText extends AppCompatEditText { + + private Bitmap mClearBitmap; + private Bitmap mPasswordVisibleBitmap; + private Bitmap mPasswordInvisibleBitmap; + + private Paint mBitmapPaint; + + /** + * the edge offset of first bitmap + */ + private int mBitmapRightEdgeOffset; + + /** + * the margin between clearing bitmap and password bitmap + */ + private int mBitmapMargin; + + private int mInitPaddingRight; + + private boolean mPasswordVisibleEnable; + private boolean mContentClearableEnable; + + private static final int DOWN_POSITION_NONE = 1; + private static final int DOWN_POSITION_CLEAR = 2; + private static final int DOWN_POSITION_PASSWORD = 3; + private int mDownPosition = DOWN_POSITION_NONE; + + private PasswordTransformationMethod mVisibleTransformation; + + public ClearableEditText(Context context) { + super(context); + init(context, null); + } + + public ClearableEditText(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public ClearableEditText(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + TypedArray typedArray = null; + try { + typedArray = context.obtainStyledAttributes(attrs, R.styleable.ClearableEditText); + + BitmapDrawable clearDrawable = (BitmapDrawable) typedArray.getDrawable(R.styleable.ClearableEditText_cet_clear_drawable); + if (clearDrawable != null) { + mClearBitmap = clearDrawable.getBitmap(); + } else { + mClearBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.base_icon_clear); + } + + BitmapDrawable passwordVisibleDrawable = (BitmapDrawable) typedArray.getDrawable(R.styleable.ClearableEditText_cet_password_seeing_drawable); + if (passwordVisibleDrawable != null) { + mPasswordVisibleBitmap = passwordVisibleDrawable.getBitmap(); + } + + BitmapDrawable passwordInvisibleDrawable = (BitmapDrawable) typedArray.getDrawable(R.styleable.ClearableEditText_cet_password_closing_drawable); + if (passwordInvisibleDrawable != null) { + mPasswordInvisibleBitmap = passwordInvisibleDrawable.getBitmap(); + } + + mPasswordVisibleEnable = typedArray.getBoolean(R.styleable.ClearableEditText_cet_enable_password_visibility_toggle, false) && isInputTypePassword(); + mContentClearableEnable = typedArray.getBoolean(R.styleable.ClearableEditText_cet_enable_content_clearable, true); + + } finally { + if (typedArray != null) { + typedArray.recycle(); + } + } + + mInitPaddingRight = getPaddingRight(); + + mBitmapRightEdgeOffset = Sizes.dip(5); + mBitmapMargin = Sizes.dip(15); + + mBitmapPaint = new Paint(); + mBitmapPaint.setAntiAlias(true); + + adjustPadding(); + addTextChangedListener(newWatcher()); + } + + private Bitmap getPasswordVisibleBitmap() { + if (mPasswordVisibleBitmap == null) { + mPasswordVisibleBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.base_icon_password_open); + } + return mPasswordVisibleBitmap; + } + + private Bitmap getPasswordInvisibleBitmap() { + if (mPasswordInvisibleBitmap == null) { + mPasswordInvisibleBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.base_icon_password_close); + } + return mPasswordInvisibleBitmap; + } + + private Bitmap getPasswordBitmap() { + if (isPasswordSeeable()) { + return getPasswordInvisibleBitmap(); + } else { + return getPasswordVisibleBitmap(); + } + } + + private PasswordTransformationMethod getVisibleTransformation() { + if (mVisibleTransformation == null) { + mVisibleTransformation = new PasswordTransformationMethod(); + } + return mVisibleTransformation; + } + + private void adjustPadding() { + boolean hasClearBitmap = mContentClearableEnable && !TextUtils.isEmpty(getTextValue()); + + int rightPadding; + if (mPasswordVisibleEnable) { + rightPadding = mInitPaddingRight + getPasswordBitmap().getWidth() + mBitmapRightEdgeOffset; + if (hasClearBitmap) { + rightPadding += (mBitmapMargin + mClearBitmap.getWidth()); + } + } else if (hasClearBitmap) { + rightPadding = mInitPaddingRight + mClearBitmap.getWidth() + mBitmapRightEdgeOffset; + } else { + rightPadding = mInitPaddingRight; + } + + invalidate(); + + setPadding(getPaddingLeft(), getPaddingTop(), rightPadding, getPaddingBottom()); + } + + @NotNull + private TextWatcher newWatcher() { + return new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable s) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + adjustPadding(); + } + }; + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + int action = event.getAction(); + + if (action == MotionEvent.ACTION_DOWN) { + mDownPosition = detectTouchPosition(event); + } else if (action == MotionEvent.ACTION_UP) { + int upPosition = detectTouchPosition(event); + Timber.d("upPosition = %d", upPosition); + if (upPosition == mDownPosition) { + if ((upPosition == DOWN_POSITION_CLEAR)) { + setText(""); + } else if (upPosition == DOWN_POSITION_PASSWORD) { + if (isPasswordSeeable()) { + setTransformationMethod(null); + } else { + setTransformationMethod(getVisibleTransformation()); + } + } + } + } + return super.onTouchEvent(event); + } + + private int detectTouchPosition(MotionEvent event) { + float eventX = event.getX(); + + if (mPasswordVisibleEnable) { + + int passwordRight = getMeasuredWidth() - mInitPaddingRight - mBitmapRightEdgeOffset; + int passwordLeft = passwordRight - getPasswordBitmap().getWidth(); + if (eventX >= passwordLeft && eventX <= passwordRight) { + return DOWN_POSITION_PASSWORD; + } + + if (mContentClearableEnable && !TextUtils.isEmpty(getTextValue())) { + int clearRight = passwordLeft - mBitmapMargin; + int clearLeft = clearRight - mClearBitmap.getWidth(); + if (eventX >= clearLeft && eventX <= clearRight) { + return DOWN_POSITION_CLEAR; + } + } + + } else if (mContentClearableEnable && !TextUtils.isEmpty(getTextValue())) { + + int clearRight = getMeasuredWidth() - mInitPaddingRight - mBitmapRightEdgeOffset; + int clearLeft = clearRight - mClearBitmap.getWidth(); + if (eventX >= clearLeft && eventX <= clearRight) { + return DOWN_POSITION_CLEAR; + } + } + + return DOWN_POSITION_NONE; + } + + private String getTextValue() { + Editable text = getText(); + return (text == null) ? "" : text.toString(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.save(); + canvas.translate(getMeasuredWidth() - mInitPaddingRight, 0); + + if (mPasswordVisibleEnable) { + Bitmap passwordBitmap = getPasswordBitmap(); + canvas.translate(-(passwordBitmap.getWidth() + mBitmapRightEdgeOffset), 0); + canvas.drawBitmap(passwordBitmap, 0, (getMeasuredHeight() - passwordBitmap.getHeight()) / 2, mBitmapPaint); + } + + boolean hasClearBitmap = mContentClearableEnable && !TextUtils.isEmpty(getTextValue()); + + if (hasClearBitmap) { + if (mPasswordVisibleEnable) { + canvas.translate(-(mClearBitmap.getWidth() + mBitmapMargin), 0); + } else { + canvas.translate(-(mClearBitmap.getWidth() + mBitmapRightEdgeOffset), 0); + } + canvas.drawBitmap(mClearBitmap, 0, (getMeasuredHeight() - mClearBitmap.getHeight()) / 2, mBitmapPaint); + } + + canvas.restore(); + } + + private boolean isInputTypePassword() { + int inputType = getInputType(); + final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION); + final boolean passwordInputType = variation == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + final boolean webPasswordInputType = variation == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD); + final boolean numberPasswordInputType = variation == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD); + return passwordInputType || webPasswordInputType || numberPasswordInputType; + } + + private boolean isPasswordSeeable() { + return getTransformationMethod() instanceof PasswordTransformationMethod; + } + + public void setInitPaddingRight(int initPaddingRight) { + mInitPaddingRight = initPaddingRight; + adjustPadding(); + } + +} \ No newline at end of file diff --git a/lib_base/src/main/res/drawable-xxhdpi/base_icon_clear.png b/lib_base/src/main/res/drawable-xxhdpi/base_icon_clear.png new file mode 100644 index 0000000000000000000000000000000000000000..8660561dac5f1a5642cc237357cbcbfdd2db3b40 GIT binary patch literal 506 zcmeAS@N?(olHy`uVBq!ia0vp^W+2SL3?z5Yp7kC`g$MYAxB}@LU~v8Vbs+up=~EyB z!iI<=%ig+m>*mdy4%IEHu}zk2O--Vp^J)(cFE z?*4DrGfSJV-Bh(j;If^HsddbevwK$Y=p}1~Jv8f?eb(0a^ZqOgkt4Q6TeoR= z?M};aN?aCF8GdSx|Ba4EJySZ>w_e*CR{^EF2jdZ!nzm@|${DKUW z7H1uKF1or>b!U_o=hXsb^(jjpiyqkBcSAwrAny^T^(x{Wm8UgNMxL*m(|e`z%>U@~ z^_S;fdHLyo*hgQ+_2vrm-!95_Jk_>5d;O`jb<(o89GA*>|598d|MM5C^Zs)$rmI(m f2J5!XpJm@RzxC3*vy*axAgTe~DWM4fkf;S* literal 0 HcmV?d00001 diff --git a/lib_base/src/main/res/drawable-xxhdpi/base_icon_password_close.png b/lib_base/src/main/res/drawable-xxhdpi/base_icon_password_close.png new file mode 100644 index 0000000000000000000000000000000000000000..9df7c8cfe87bfca330db3c208e050827fab39f25 GIT binary patch literal 1613 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 literal 0 HcmV?d00001 diff --git a/lib_base/src/main/res/values/base_attrs.xml b/lib_base/src/main/res/values/base_attrs.xml index cab07bc..5b9093b 100644 --- a/lib_base/src/main/res/values/base_attrs.xml +++ b/lib_base/src/main/res/values/base_attrs.xml @@ -2,108 +2,108 @@ - - + + - - + + - + - + - + - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + - - + - - - + + + - - - + + + - - - + + + - - - + + + - + - + - + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file