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