练习:仿支付宝支付密码EditText

825 阅读5分钟

1.先上效果图(文末有源码):

2.整体思路:

在一个Edittext上面绘制一层前景,覆盖在Edittext上,然后监听文本输入事件,在对应的位置画圆

上图五行控件代表step0 ~ step4

step0:原生EditText控件

step1:在原生EditText控件上绘制一个白色的实心圆角矩形

step2:在step1基础上绘制一个黑色的空心圆角矩形

step3:绘制分割线

step4:根据输入的内容绘制黑色圆点

3.代码实现:

//自定义一个EditText,继承自EditText
public class PayPassWordEditText extends android.support.v7.widget.AppCompatEditText

//重写onDraw方法,并在onDraw方法中绘制出一个白色的圆角矩形
 @Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // step1:绘制白色背景
    
    //将画笔设置成白色
    mPaint.setColor(Color.WHITE);
    //填充模式设置成FILL
    mPaint.setStyle(Paint.Style.FILL);
    //设置线宽
    mPaint.setStrokeWidth(strokeWidth);
    //绘制一个圆角矩形
    canvas.drawRoundRect(mRect.left + strokeWidth / 2, mRect.top + strokeWidth / 2, mRect.right - strokeWidth / 2, mRect.bottom - strokeWidth / 2, radius, radius, mPaint);
    
    // step2:绘制黑色边框,大体跟第一步一样
    
    //将画笔设置成黑色
    mPaint.setColor(Color.BLACK);
    //设置填充模式为STROKE
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(strokeWidth);
    canvas.drawRoundRect(mRect.left + strokeWidth / 2, mRect.top + strokeWidth / 2, mRect.right - strokeWidth / 2, mRect.bottom - strokeWidth / 2, radius, radius, mPaint);
    
}

写到这里第一步和第二步就实现了,接下来要绘制五条分割线

    //定义密码位数相关变量
    private int digit = 6;// 六位数密码
    //定义每个格子的宽度
    private float averageLength;
    //定义分割线位置数组,数组长度就是密码长度
    private float[] lineIndex;
    
    //重写onSizeChanged获取控件宽高信息
    
     @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //获取控件宽度
        width = w;
        //获取控件高度
        height = h;
        //定义一个RectF,用于绘制圆角矩形
        mRect = new RectF(0, 0, width, height);
        //初始化分割线位置数组,数组长度就是密码长度
        lineIndex = new float[digit];
        // 平均每个格子的宽度 = 宽/密码长度
        averageLength = width / digit;
        //对lineIndex数组进行循环赋值
        for (int i = 0; i < lineIndex.length; i++) {
            //每条分割线的X轴坐标为 :(i+1)*平均每个格子的宽度
            lineIndex[i] = (i + 1) * averageLength;
        }
        Log.e("onSizeChanged width", width + "");
        Log.e("onSizeChanged height", height + "");
    }
    
    
    /**
     * 绘制分割线方法,在onDraw方法中调用
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        //六位数密码只需要绘制五条分割线,所以这里需要length-1
        for (int i = 0; i < lineIndex.length-1; i++) {
            //根据每条分割线的x坐标进行绘制
            canvas.drawLine(lineIndex[i], mRect.top, lineIndex[i], mRect.bottom, mPaint);
        }
    }
    
     @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // step1:绘制白色背景
        ...
        // step2:绘制黑色边框
        ...
        // step3:绘制分割线
        drawLine(canvas);
    
    }

到这一步就有了支付宝密码支付控件的样子了,但是还需要监听EditText的文本输入,然后在对应的位置绘制小圆点

//用于保存EditText输入的字符串
private String afterText = null;

//实现TextWatcher接口
public class PayPassWordEditText extends android.support.v7.widget.AppCompatEditText implements TextWatcher

//主要对afterTextChanged方法进行修改
    @Override
    public void afterTextChanged(Editable s) {
        //获取EditText上输入的文本
        String resultText = s.toString();
        //判断文本长度,如果等于密码位数,弹一个Toast显示密码
        if (resultText.length() == digit) {
            Toast.makeText(getContext(), "密码是:" + s.toString(), Toast.LENGTH_SHORT).show();
        //如果大于密码位数
        } else if (resultText.length() > digit) {
            //就对文本进行截取,从0位截取到第digit位
            resultText = s.toString().substring(0, digit);
            //重新进行赋值操作
            // 这里需要注意,在afterTextChanged中进行setText操作容易造成死循环,需要控制好判断条件
            setText(resultText);
        }
        //设置光标位置为最后
        setSelection(resultText.length());
        //为全局变量赋值
        afterText = resultText;
        //重绘操作
        invalidate();
    }
    
    
    
    /**
     * 绘制圆点操作,需要在onDraw方法中调用
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        //判空
        if(afterText==null){
            return;
        }
        //定义圆心坐标,圆形Y坐标恒定为控件高度的一半
        float x, y = height / 2;
        //设置画笔模式为FILL
        mPaint.setStyle(Paint.Style.FILL);
        //设置画笔颜色
        mPaint.setColor(Color.BLACK);
        //根据afterText的长度进行循环
        for (int i = 0; i < afterText.length(); i++) {
            //每一个圆形的x坐标为:分割线的坐标 - 平均每个格子宽度/2
            x = lineIndex[i] - averageLength / 2;
            //绘制圆点
            canvas.drawCircle(x, y, 10, mPaint);
        }
    }
    
    
  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // step1:绘制白色背景
        ...
        // step2:绘制黑色边框
        ...
        // step3:绘制分割线
        drawLine(canvas);
        
        // step4:绘制圆点
        drawCircle(canvas);
    }

4.运行,下班。

整体源代码:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.Toast;

public class PayPassWordEditText extends android.support.v7.widget.AppCompatEditText implements TextWatcher {


    private float width;
    private float height;
    private RectF mRect;
    private float radius = 10;
    private Paint mPaint;
    private int digit = 6;// 六位数密码
    private float[] lineIndex;
    float averageLength;

    private String afterText = null;

    private float strokeWidth = 3;

    public PayPassWordEditText(Context context) {
        super(context);
        init();
    }

    public PayPassWordEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PayPassWordEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        mRect = new RectF(0, 0, width, height);

        lineIndex = new float[digit];
        averageLength = width / digit;
        for (int i = 0; i < lineIndex.length; i++) {
            lineIndex[i] = (i + 1) * averageLength;
        }

        Log.e("onSizeChanged width", width + "");
        Log.e("onSizeChanged height", height + "");
    }

    private void init() {
        setOnLongClickListener(null);
        setCursorVisible(false);
        setInputType(InputType.TYPE_CLASS_NUMBER);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        setTextColor(Color.BLACK);
        addTextChangedListener(this);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(Color.WHITE);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(strokeWidth);
        canvas.drawRoundRect(mRect.left + strokeWidth / 2, mRect.top + strokeWidth / 2, mRect.right - strokeWidth / 2, mRect.bottom - strokeWidth / 2, radius, radius, mPaint);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(strokeWidth);
        canvas.drawRoundRect(mRect.left + strokeWidth / 2, mRect.top + strokeWidth / 2, mRect.right - strokeWidth / 2, mRect.bottom - strokeWidth / 2, radius, radius, mPaint);
        drawLine(canvas);
        drawCircle(canvas);
    }

    /**
     * 绘制分割线方法
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        //六位数密码只需要绘制五条分割线,所以这里需要length-1
        for (int i = 0; i < lineIndex.length-1; i++) {
            //根据每条分割线的x坐标进行绘制
            canvas.drawLine(lineIndex[i], mRect.top, lineIndex[i], mRect.bottom, mPaint);
        }
    }


    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
    }

    @Override
    public void afterTextChanged(Editable s) {
        //获取EditText上输入的文本
        String resultText = s.toString();
        //判断文本长度,如果等于密码位数,弹一个Toast显示密码
        if (resultText.length() == digit) {
            Toast.makeText(getContext(), "密码是:" + s.toString(), Toast.LENGTH_SHORT).show();
        //如果大于密码位数
        } else if (resultText.length() > digit) {
            //就对文本进行截取,从0位截取到第digit位
            resultText = s.toString().substring(0, digit);
            //重新进行赋值操作
            // 这里需要注意,在afterTextChanged中进行setText操作容易造成死循环,需要控制好判断条件
            setText(resultText);

        }
        //设置光标位置为最后
        setSelection(resultText.length());
        //为全局变量赋值
        afterText = resultText;
        //重绘操作
        invalidate();
    }

    /**
     * 绘制圆点操作
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        //判空
        if(afterText==null){
            return;
        }
        //定义圆心坐标,圆形Y坐标恒定为控件高度的一半
        float x, y = height / 2;
        //设置画笔模式为FILL
        mPaint.setStyle(Paint.Style.FILL);
        //设置画笔颜色
        mPaint.setColor(Color.BLACK);
        //根据afterText的长度进行循环
        for (int i = 0; i < afterText.length(); i++) {
            //每一个圆形的x坐标为:分割线的坐标 - 平均每个格子宽度/2
            x = lineIndex[i] - averageLength / 2;
            //绘制圆点
            canvas.drawCircle(x, y, 10, mPaint);
        }
    }
}