Android custom View-imitate Alipay payment password box, custom password input box

According to the convention, the effect picture is first:
Custom password input box

⚠️Analysis:

1. Need to call the input number input box
2. Monitor the input box
3. Draw a rectangle with rounded corners
4. Draw a dividing line
5. Draw the origin of the password

Ideas:

EditText meets the first two requirements, but we don't want to display the input content and the default password style. So we can customize the View to inherit EdiText. To achieve the following three requirements.
1. We only need EditText to call the digital input box and the monitoring feature of the text, and we can customize the others

 android:inputType="number"

2. In the onDraw method, we draw a rounded rectangle pen to fill the mode to cover the original EdiText display effect, and then draw a smaller rounded rectangle inside the just-out rounded rectangle to achieve the rounded rectangle border. (In fact, there are various implementation methods)
3. Then draw the "password length-1" dividing line according to the password length.
4. Rewrite the monitoring text to draw the dots of the password.

The code is as follows: there are detailed comments in the code


public class PasswordInputView extends EditText {
    private String TAG = "PasswordInputView";
    private Context mContext;
    private int passwordSize = 6;//密码的个数这里是默认值,也可以通过自定义属性来设置其他数值
    private int borderWidth = 6;//密码输入框的边框宽度px
    private int borderRadius = 6;//密码输入框矩形圆角的半径
    private int borderColor = 0xff333333;//密码输入框边框的颜色
    private int passwordWidth = 12;//密码的宽度
    private int passwordColor = 0xff000000;//密码的颜色
    private int defaultSplitLineWidth = 3; //px

    private int allPasswordWidth;//控件的总宽度
    private int allPasswordHight;//控件的总高度

    private Paint mPaint;//边框画笔
    private Paint pwPaint;//密码画笔

    private int passwordTextSize;//输入密码的个数
    private String password;//密码

    private onPasswordChangedListener onPasswordChangedListener;

    public void setOnPasswordChangedListener(PasswordInputView.onPasswordChangedListener onPasswordChangedListener) {
        this.onPasswordChangedListener = onPasswordChangedListener;
    }


    //获取密码
    public String getPassword() {
        return password;
    }

    //注意 :只能复写这个构造函数,切只能在这里初始化,不然你会发现EditText的特性就没有来
    public PasswordInputView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }


    private void init(Context context, AttributeSet attrs) {
        mContext = context;
        /**
         * 初始化各种自定义属性
         * */
        TypedArray typedArray = mContext.obtainStyledAttributes(attrs,R.styleable.PasswordInputView);
        passwordSize = typedArray.getInt(R.styleable.PasswordInputView_passwordLength, passwordSize);
        borderWidth = typedArray.getDimensionPixelSize(R.styleable.PasswordInputView_borderWidth, borderWidth);
        borderRadius = typedArray.getDimensionPixelSize(R.styleable.PasswordInputView_borderRadius, borderRadius);
        borderColor = typedArray.getColor(R.styleable.PasswordInputView_borderColor, borderColor);
        passwordWidth = typedArray.getDimensionPixelSize(R.styleable.PasswordInputView_passwordWidth, passwordWidth);
        passwordColor = typedArray.getColor(R.styleable.PasswordInputView_passwordColor, passwordColor);
        Log.d(TAG,"borderRadius:"+borderRadius);
        Log.d(TAG,"passwordSize:"+passwordSize);
        typedArray.recycle();

        //初始化边框画笔
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(borderWidth);

        //初始化密码画笔
        pwPaint = new Paint();
        pwPaint.setAntiAlias(true);
        pwPaint.setStyle(Paint.Style.FILL);
        pwPaint.setColor(passwordColor);
        pwPaint.setStrokeWidth(passwordWidth);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.d(TAG, "onSizeChanged");
        allPasswordWidth = w;
        allPasswordHight = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.d(TAG, "onDraw");


        Log.d(TAG, "allPasswordWidth:" + allPasswordWidth);
        Log.d(TAG, "borderRadius:" + borderRadius);
        //画一个矩形遮住显示的文字
        mPaint.setColor(borderColor);
        canvas.drawRoundRect(0, 0, allPasswordWidth, allPasswordHight, borderRadius, borderRadius, mPaint);
        //再画一个白色矩形这种上面的矩形内部,形成假的边框
        mPaint.setColor(Color.WHITE);
        canvas.drawRoundRect(defaultSplitLineWidth, defaultSplitLineWidth,
                allPasswordWidth - defaultSplitLineWidth, allPasswordHight - defaultSplitLineWidth,borderRadius,borderRadius, mPaint);

        //画分割线
        int lineStartAndEndx = allPasswordWidth / passwordSize;
        int lineStarty = 0;
        int lineendy = allPasswordHight;
        mPaint.setColor(borderColor);
        mPaint.setStrokeWidth(defaultSplitLineWidth);
        for (int i = 1; i < passwordSize; i++) {
            canvas.drawLine(lineStartAndEndx, lineStarty, lineStartAndEndx, lineendy, mPaint);
            lineStartAndEndx += allPasswordWidth / passwordSize;
        }

        //画密码圆
        int circleX = allPasswordWidth / passwordSize / 2;
        int circleY = allPasswordHight / 2;
        if (passwordTextSize > 0) {
            for (int i = 0; i < passwordTextSize; i++) {
                canvas.drawCircle(circleX, circleY, passwordWidth, pwPaint);
                circleX += allPasswordWidth / passwordSize;
            }

        }
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
        //监听文字变化大于密码设定长度不记录
        Log.d(TAG,"onTextChanged:"+passwordSize);
        if (text.length() <= passwordSize){
            passwordTextSize = text.length();
            password = text.toString();
            if (onPasswordChangedListener != null)
                onPasswordChangedListener.setPasswordChanged();
        }else {
            text = password;
        }
        invalidate();

    }

    //设置监听密码变化
    public interface onPasswordChangedListener {
        public void setPasswordChanged();
    }
}

Custom attribute file attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PasswordInputView">
        <attr name="passwordLength" format="integer"/>
        <attr name="borderWidth" format="dimension"/>
        <attr name="borderRadius" format="dimension"/>
        <attr name="borderColor" format="color"/>
        <attr name="passwordWidth" format="dimension"/>
        <attr name="passwordColor" format="color"/>
    </declare-styleable>
</resources>

Use in layout

android:maxLength="6" The limited length of the
password app:passwordLength="6" The middle length of the password
These two should be set the same, this point can be optimized, just set a value.

<com.goodboy.mile.View.PasswordInputView
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:id="@+id/password_view"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@null"
        android:cursorVisible="false"
        android:inputType="number"
        android:maxLength="6"
        app:passwordLength="6"
        app:borderWidth="10dp"
        app:borderRadius="8dp"
        app:passwordWidth="6dp"
        app:passwordColor="#ffaaaaaa"/>

Guess you like

Origin blog.csdn.net/yanwenyuan0304/article/details/95060901