Android高效率实现自定义密码输入效果,仿微信和支付宝密码输入效果

今天使用支付宝支付时突然发现支付页面输入密码效果感觉很棒 , 又打开微信支付页面发现效果也类似,于是乎我利用星期日休息时间仿照效果也做了一个Demo , 废话不多说了直接上效果图
效果图如下:

Android高效率实现自定义密码输入效果,仿微信和支付宝密码输入效果

文章插图
简单概要:
实现带动画效果,当输入长度增加时,圆变大,当输入长度减少时,圆变小,当输入完后自动会回调方法给调用者 。密码输入长度通过属性设置:=”6”
注意事项:
自定义类继承实现.接口代码中通过反射获取写类继承类获取百分比值实现动画效果写回调接口
遇到的问题:
当输入长度增加,再此减少长度 , 再次增加长度后会发现会自动增加两个圆,减少也是,产生原因:获取当前长度有误 , 解决办法:在判断减少减少状态时,把获取到的当前长度+1当输入完会执行多次回调方法,产生原因:快速输入内容到最长度,因为动画没执行完,当动画执行完会多执行一次回调,解决办法:使用标识符判断
接下来开始上代码分析:
首先布局文件:

这里必须设置:=”6”
自定义MyEditText类继承EditText
Android高效率实现自定义密码输入效果,仿微信和支付宝密码输入效果

文章插图
/*** 画线*/private Paint linePaint;/*** 画圆*/private Paint cPaint;/*** 控件宽度*/private int width;/*** 控件高度*/private int height;/*** 绘制框内填充色*/private Paint kPaintColor;/*** 记录框内颜色矩形大小*/private RectF krectF;/*** 记录外轮框矩形大小*/private RectF rectF;/*** 获取最大可以输入的长度*/private int maxLength;/*** 给边距值 , 因为线粗*/private float padding = 1f;/*** 四周倒角半径*/private float chamferRadius = 16f;/*** 圆半径*/private float radius = 22;/*** 百分比,用于计算圆的大小*/private float percent = 0f;/*** 控制框内背景色*/private String backgroundColor = "#eeeeee";/*** 动画类*/private MyAnimation animationy;/*** 运行一次*/private boolean isFirst = false;/*** 标识使回调方法执行一次,当执行圆减少时恢复可执行回调方法*/private boolean isFirstComplete = false;/*** 用于记录圆个数是增加还是减少*/private boolean isAdd = true;/*** 记录上次输入内容的长度*/private int length = 0;/*** 记录当前输入内容的长度*/private int currentLength = 0;/*** 记录当前输入的内容*/private CharSequence texts;
上面属性标注很明细了,就不再解说了
接下来通过反射获取系统中mMax字段值
/*** 获取EditText输入框内允许输入的最大个数,通过属性必须设置值* @return*/private int getMaxLength() {//系统提供的方法,查看源码可知字段mMax保存在LengthFilter类内,LengthFilter类是InputFilter内部实现类InputFilter[] filters = getFilters();for(InputFilter filter : filters) {if("android.text.InputFilter.LengthFilter".equals(filter.getClass().getCanonicalName())) {try {Field mMax = filter.getClass().getDeclaredField("mMax");mMax.setAccessible(true);//获取字段值int num = (int)mMax.get(filter);return num;} catch (Exception e) {e.printStackTrace();}}}return -1;}
通过查看源码可知,是接口,在接口内部有类 , 是实现类,在类中定义字段mMax,通过反射得到值
public MyEditText(Context context) {this(context,null);}public MyEditText(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}public MyEditText(Context context, AttributeSet attrs) {this(context, attrs,0);}
上面是构造函数,其中init()方法是初始化函数具体代码如下
【Android高效率实现自定义密码输入效果,仿微信和支付宝密码输入效果】/*** 初始化数据*/private void init() {//隐藏光标setCursorVisible(false);//获取焦点requestFocus();setFocusableInTouchMode(true);setFocusable(true);//绘制外边边框和中间线条linePaint = new Paint();//设置抗锯齿linePaint.setAntiAlias(true);//设置样式linePaint.setStyle(Paint.Style.STROKE);//绘制框内填充色kPaintColor = new Paint();kPaintColor.setAntiAlias(true);//设置框内颜色kPaintColor.setColor(Color.parseColor(backgroundColor));//设置填充kPaintColor.setStyle(Paint.Style.FILL);//绘制圆cPaint = new Paint();cPaint.setStyle(Paint.Style.FILL);cPaint.setAntiAlias(true);//创建动画animationy = new MyAnimation();animationy.setDuration(200);animationy.setAnimationListener(this);}
上面标注很明细了,可以直接看标准,不详细解释
接下来覆写方法 , 在其中获取控件宽高等
@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);//让代码执行一次if(!isFirst && changed) {//记录控件当前宽度width = getWidth();//记录控件当前高度height = getHeight();//绘制框内颜色krectF = new RectF(padding,padding,width-padding,height-padding);//绘制外轮廓rectF = new RectF(padding,padding,width-padding,height-padding);maxLength = getMaxLength();//只允许执行一次isFirst = true;}}
接下来覆写方法进行设置标识符确认圆是增加还是减少状态
@Overrideprotected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {super.onTextChanged(text, start, lengthBefore, lengthAfter);//记录内容,用于传给回调函数texts = text;//获取当前文本输入的长度currentLength = text.length();if(currentLength >= length) {//增加圆isAdd = true;}else {//减少圆isAdd = false;//当减少圆时,恢复完成时的回调isFirstComplete = false;//防止当为减少时,圆瞬间减少两个currentLength = currentLength + 1;}//回调函数,给调用者if(listener != null) {if(currentLength == 1 && !isAdd) {//防止输入框内没有值时,调用者仍可获取到第一个输入值listener.onTextChanged("");}else {listener.onTextChanged(text);}}if(currentLength <= getMaxLength()) {if(animationy != null) {//每次进来清除上次动画clearAnimation();//开启新动画startAnimation(animationy);}else {invalidate();}}//记录上次输入内容的长度length = currentLength;}
Android高效率实现自定义密码输入效果,仿微信和支付宝密码输入效果

文章插图
当内容改变时系统调用方法,用记录上次长度,用当前长度和上次长度进行比较判断,如果当前长度大于上次长度说明圆增加,把isAdd标识设置为true,否则反之,这里需要注意的是当圆为减少时,此时需要把当前长度+1 , 要不然绘制圆时会出现同时增加两个圆或者减少两个圆现象,原因是在绘制圆时for循环中当前长度少一个圆引起的,详细见for循环
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制框内颜色canvas.drawRoundRect(krectF,chamferRadius,chamferRadius,kPaintColor);//绘制外轮廓canvas.drawRoundRect(rectF,chamferRadius,chamferRadius,linePaint);//计算每个输入框宽float x = width / maxLength;//计算每个输入框高float y = height;//画线for(int i = 1; i < maxLength; i++) {//画框内竖线canvas.drawLine(x*i,padding,x*i,y - padding,linePaint);}for(int i = 0; i < currentLength; i++) {if(isAdd) {//增加圆isAdd = true;if(i < currentLength-1) {//画圆,不为当前圆时,不需要产生动画效果canvas.drawCircle(x/2+x*i,y/2,radius,cPaint);}else if(i == currentLength-1) {//画圆,当画当前圆时,需要产生扩大效果canvas.drawCircle(x/2+x*i,y/2,radius*percent,cPaint);}}else {//减少圆isAdd = false;if(i < currentLength - 1) {//画圆,不为当前圆时,不需要产生动画效果canvas.drawCircle(x/2+x*i,y/2,radius,cPaint);}else if(i == currentLength - 1) {//画圆,当画当前圆时,需要产生缩小效果canvas.drawCircle(x/2+x*i,y/2,radius-radius*percent,cPaint);}}}}
上面使用两个for循环,第一个for循环绘制竖线,第二个for循环是绘制圆 , 在for循环中通过判断当前isAdd是增加圆还是减少圆来进行绘制,需要注意是减少圆时的个数
/*** 动画开始时,回调此方法* @param animation*/@Overridepublic void onAnimationStart(Animation animation) {}/*** 动画结束时回调次方法* @param animation*/@Overridepublic void onAnimationEnd(Animation animation) {//动画完成执行if(isAdd && currentLength == getMaxLength() && listener != null && !isFirstComplete) {//最后一个圆执行完动画后,执行此代码,为了保证只允许执行一次,添加个isFirstComplete标识用于判断isFirstComplete = true;listener.onComplete(texts);}}/*** 动画重复执行时,回调此方法* @param animation*/@Overridepublic void onAnimationRepeat(Animation animation) {}
上边方法为实现接口时需要实现的方法 , 其中需要注意的是在动画结束后去执行自定义回调方法,防止动画没执行完就执行回调
/*** 执行动画*/private class MyAnimation extends Animation {@Overrideprotected void applyTransformation(float interpolatedTime, Transformation t) {super.applyTransformation(interpolatedTime, t);//记录百分比值percent = interpolatedTime;//百分比变化 , 需要更新页面postInvalidate();}}
上边类为继承动画类 , 主要是为了得到方法,参数是系统传回来的运动距离的百分值
接下来自定义完成时的接口
public EditTextContentListener listener;/*** 设置输入内容监听接口* @param listener*/public void setOnEditTextContentListener(EditTextContentListener listener) {this.listener = listener;}/*** 输入内容监听接口,提供给调用者*/public interface EditTextContentListener {/*** 完成时,回调此方法* @param text*/public void onComplete(CharSequence text);/*** 输入框内容改变时,回调次方法* @param text*/public void onTextChanged(CharSequence text);}
上面注解很详细,接下来类
public class MainActivity extends AppCompatActivity {private MyEditText myEditText;private TextView textView;private TextView textViewComplete;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);textView = (TextView) findViewById(R.id.TextView);textViewComplete = (TextView) findViewById(R.id.TextView_complete);myEditText = (MyEditText) findViewById(R.id.MyEditText);myEditText.setOnEditTextContentListener(new MyEditText.EditTextContentListener() {@Overridepublic void onComplete(CharSequence text) {if(text != null) {textViewComplete.setText("输入完成,内容为:"+text);}}@Overridepublic void onTextChanged(CharSequence text) {if(text != null) {textViewComplete.setText("");textView.setText("输入内容为:"+text);}}});}
如果大家还有什么疑问 , 请在下方留言 。
如何有不足的地方希望大家指出来,共同学习 。
源码下载,请点击这里!