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);
}
}
}