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