parent
8261c7af25
commit
115715f2f5
@ -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(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
After Width: | Height: | Size: 506 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 2.4 KiB |
Loading…
Reference in new issue