Android密码字符为真,Android:仿支付宝交易密码框

App需要支付场景的时候,都会让用户输入密码交易框,如果用系统或者第三方键盘可能导致密码泄露。因此,比较多的App会自定义密码输入键盘来提供安全性。本文依照支付宝密码输入界面来设计,同时提供随机键盘功能。

项目地址:Github

看效果图

bf0d74e761e7

密码输入框效果图

模拟器上,会导致线条绘制模糊。真机运行效果更棒。

数字密码键盘安全级别

低:系统提供键盘或者第三方输入法键盘。

中:自定义键盘,但不随机键盘数字。

高:自定义键盘,且随机键盘数字。

数字密码键盘实现原理

键盘实现的原理大致有两种方法。

1.利用系统提供的KeyboardView来完成。

2.另外一种就是自定义View来完成键盘绘制。

本文采取的自定义View方式绘制键盘。

因为数字键盘是九宫格的排列,所以首先考虑使用GridLayout。

因为GridLayout并不提供均匀分布的功能,所以我们得重新填充Child View。

解决思路如下:

1.填充键盘Key。

2.绘制键盘之间的分割线。

3.完成键盘Key点击事件监听。

4.输出键盘密码的输入。

看键盘源码

public class PasswordKeyboard extends GridLayout implements View.OnClickListener, View.OnTouchListener {

public static final String DEL = "删除";

public static final String DONE = "OK";

//因为UED是给的是iPhone设计稿,所以是按照等比的思想设置键盘Key的高度和宽度

private static final int IPHONE = 779;

//每个键盘Key的宽度,为屏幕宽度的三分之一

private int keyWidth = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth() / 3;

//每个键盘Key的高度

private int keyHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight() * 59 / IPHONE;

private int screenWidth = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();

private Paint mPaint;

//List集合存储Key,方便每次输错都能再次随机数字键盘

private final List keyButtons = new ArrayList<>();

private WorkHandler mWorkHandler;

private static final int DELETE = 1;

//WorkHandler 用于处理长按"删除"Key时,执行重复删除操作。

private static class WorkHandler extends Handler {

private int index = 0;

int diffTime = 100;

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

switch (msg.what) {

case DELETE:

PasswordKeyboard numberKeyBoard = (PasswordKeyboard) msg.obj;

numberKeyBoard.handlerClick(DEL);

removeMessages(DELETE);

Message message = obtainMessage(DELETE);

message.obj = numberKeyBoard;

if (diffTime > 40) {

diffTime = diffTime - index;

}

sendMessageDelayed(message, diffTime);

index++;

break;

}

}

public void reset() {

index = 0;

diffTime = 100;

}

}

public PasswordKeyboard(Context context) {

super(context);

initView();

}

public PasswordKeyboard(Context context, AttributeSet attrs) {

super(context, attrs);

initView();

}

public PasswordKeyboard(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

initView();

}

@Override

protected void onMeasure(int widthSpec, int heightSpec) {

super.onMeasure(widthSpec, heightSpec);

setMeasuredDimension(screenWidth, keyHeight * 4);

}

//重新设置键盘key位置

public void resetKeyboard() {

List keyList = randomKeys(10);

for (int i = 0; i < keyList.size(); i++) {

keyButtons.get(i).setText(keyList.get(i));

keyButtons.get(i).setTag(keyList.get(i));

}

}

private void initView() {

//必须设置调用该方法,不然onDraw方法不执行。如果ViewGroup没有背景,则其onDraw方法不执行

setWillNotDraw(false);

if (getChildCount() > 0) {

keyButtons.clear();

removeAllViews();

}

//获取随机键盘数字的字符串

List keyList = randomKeys(10);

//填充键盘Key,用Button来完成Key功能

for (int i = 0; i < keyList.size(); i++) {

Button item = new Button(getContext());

ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(keyWidth, keyHeight);

item.setLayoutParams(params);

item.setOnClickListener(this);

item.setText(keyList.get(i));

item.setBackgroundDrawable(getResources().getDrawable(R.drawable.key_selector));

//监听"删除"的长按监听事件,完成重复删除操作

if (DEL.equals(keyList.get(i))) {

item.setOnTouchListener(this);

}

item.setTag(keyList.get(i));

addView(item);

keyButtons.add(item);

}

if (mPaint == null) {

mPaint = new Paint();

mPaint.setColor(Color.parseColor("#cccccc"));

mPaint.setStrokeWidth(1);

}

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//绘制分割线

canvas.drawLine(0, getMeasuredHeight() / 4, getMeasuredWidth(), getMeasuredHeight() / 4, mPaint);

canvas.drawLine(0, 2 * getMeasuredHeight() / 4, getMeasuredWidth(), 2 * getMeasuredHeight() / 4, mPaint);

canvas.drawLine(0, 3 * getMeasuredHeight() / 4, getMeasuredWidth(), 3 * getMeasuredHeight() / 4, mPaint);

canvas.drawLine(getMeasuredWidth() / 3, 0, getMeasuredWidth() / 3, getMeasuredHeight(), mPaint);

canvas.drawLine(2 * getMeasuredWidth() / 3, 0, 2 * getMeasuredWidth() / 3, getMeasuredHeight(), mPaint);

}

@Override

public void onClick(View v) {

String character = v.getTag().toString();

handlerClick(character);

}

private void handlerClick(String character) {

//密码字符输出回调

if (mListener != null) {

if (DONE.equals(character)) {

mListener.onInput(DONE);

} else if (DEL.equals(character)) {

mListener.onInput(DEL);

} else {

mListener.onInput(character);

}

}

}

//生产键盘Key随机数字

private List randomKeys(int no) {

int[] keys = new int[no];

for (int i = 0; i < no; i++) {

keys[i] = i;

}

Random random = new Random();

for (int i = 0; i < no; i++) {

int p = random.nextInt(no);

int tmp = keys[i];

keys[i] = keys[p];

keys[p] = tmp;

}

List keyList = new ArrayList<>();

for (int key : keys) {

keyList.add(String.valueOf(key));

}

//将空字符串插入到第10个位置,是个无操作的Key

keyList.add(9, "");

//将删除字符串插入最后

keyList.add(DEL);

return keyList;

}

public void setOnPasswordInputListener(OnPasswordInputListener listener) {

this.mListener = listener;

}

private OnPasswordInputListener mListener;

@Override

public boolean onTouch(View v, MotionEvent event) {

if (mWorkHandler == null) {

mWorkHandler = new WorkHandler();

}

if (MotionEvent.ACTION_DOWN == event.getAction()) {

Message msg = mWorkHandler.obtainMessage(DELETE);

msg.obj = this;

mWorkHandler.sendMessageDelayed(msg, 500);

} else if (MotionEvent.ACTION_UP == event.getAction()) {

mWorkHandler.removeMessages(DELETE);

mWorkHandler.reset();

} else if (MotionEvent.ACTION_CANCEL == event.getAction()) {

mWorkHandler.removeMessages(DELETE);

mWorkHandler.reset();

} else if (MotionEvent.ACTION_MOVE == event.getAction()) {

} else {

//do nothing

}

return false;

}

public interface OnPasswordInputListener {

void onInput(String number);

}

}

密码显示框

因为不能明文显示输入,所以我们用“●”代替每位密码。自定义密码显示框比较简单,直接采取继承View方式完成。

解决思路如下。

1.支持密码位数设置。

2.绘制边框和分割线。

3.绘制“●”。

4.每次密码输入改变,重新绘制“●”。

看源码

public class PasswordView extends View {

private int passwordCount;

private int strokeColor;

private Paint mCirclePaint;

private Paint mPaint;

private int symbolColor;

private float mRadius;

private float inputBoxStroke;

private StringBuffer mText;

public PasswordView(Context context) {

super(context);

}

public PasswordView(Context context, AttributeSet attrs) {

super(context, attrs);

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.inputBox);

//支持某些属性设置,比如密码位数,边框颜色、宽度,"●"的颜色、大小

passwordCount = ta.getInteger(R.styleable.inputBox_passwordCount, 6);

strokeColor = ta.getColor(R.styleable.inputBox_stokeColor, Color.GRAY);

symbolColor = ta.getColor(R.styleable.inputBox_symbolColor, Color.BLACK);

mRadius = ta.getDimension(R.styleable.inputBox_symbolRadius, 12);

inputBoxStroke = ta.getDimension(R.styleable.inputBox_inputBoxStroke, 1f);

//设置输入框圆角边框

GradientDrawable gd = new GradientDrawable();

gd.setColor(Color.WHITE);

gd.setStroke((int) inputBoxStroke, strokeColor);

gd.setCornerRadius(8);

setBackgroundDrawable(gd);

ta.recycle();

if (mPaint == null) {

mPaint = new Paint();

mPaint.setColor(strokeColor);

mPaint.setStrokeWidth(inputBoxStroke);

}

if (mCirclePaint == null) {

mCirclePaint = new Paint();

mCirclePaint.setColor(symbolColor);

mCirclePaint.setStyle(Paint.Style.FILL);

}

}

public PasswordView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

int singleWidth = getMeasuredWidth() / passwordCount;

int height = getMeasuredHeight();

//绘制每个"●"之间的分割线

for (int i = 1; i < passwordCount; i++) {

canvas.drawLine(singleWidth * i, 0, singleWidth * i, height, mPaint);

}

if (mText != null) {

//绘制"●"

int textSize = mText.length() > passwordCount ? passwordCount : mText.length();

for (int i = 1; i <= textSize; i++) {

canvas.drawCircle(singleWidth * i - singleWidth / 2, height / 2, mRadius, mCirclePaint);

}

}

}

public int getPasswordCount() {

return passwordCount;

}

//支持密码位数设置

public void setPasswordCount(int passwordCount) {

this.passwordCount = passwordCount;

}

//密码改变,重新绘制

public void setPassword(CharSequence text) {

mText = (StringBuffer) text;

if (text.length() > passwordCount) {

mText.delete(mText.length() - 1, mText.length());

return;

}

postInvalidate();

}

public void clearPassword() {

if (mText != null) {

mText.delete(0, mText.length());

}

}

public CharSequence getPassword() {

return mText;

}

}

进度条绘制

支付宝的验证密码过程的进度条是采取material design风格,不过做了特殊动画效果,当验证密码正确的时候,出现打“✔️”的动画。需解决问题如下。

1.material design进度条旋转的动画。

2.停止旋转动画,开始打“✔️”动画。

3.打“✔️”动画完成的回调。

看源码

进度条旋转动画过程,是参考网上一个例子,具体出处忘记了。若原作者看见,请见谅。

public class MDProgressBar extends View {

private final static String TAG = MDProgressBar.class.getSimpleName();

private static final float DEFAULT_MAX_ANGLE = -305f;

private static final float DEFAULT_MIN_ANGLE = -19f;

//默认的动画时间

private static final int DEFAULT_DURATION = 660;

private final static int DEFAULT_ARC_COLOR = Color.BLUE;

//圆弧颜色

private int arcColor = DEFAULT_ARC_COLOR;

private AnimatorSet animatorSet;

private float mBorderWidth;

private Paint mPaint;

private RectF arcRectF;

private float startAngle = -45f;

private float sweepAngle = -19f;

private float incrementAngele = 0;

//是否需要开始绘制对勾

private boolean isNeedTick = false;

private int mResize;

private TickAnimation mTickAnimation;

//判断"对勾"动画是否过半,"对勾"由两条线绘制而成。

private boolean isAnimationOverHalf = false;

//圆形进度条的半径

private float mRadius;

private float startY1;

private float startX1;

private float stopX1;

private float stopY1;

private float stopX2;

private float stopY2;

private OnPasswordCorrectlyListener mListener;

public MDProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

init(context, attrs);

}

public MDProgressBar(Context context, AttributeSet attrs) {

super(context, attrs);

init(context, attrs);

}

private void init(Context context, AttributeSet attrs) {

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.materialStatusProgressAttr);

arcColor = typedArray.getColor(R.styleable.materialStatusProgressAttr_arcColor, Color.parseColor("#4a90e2"));

mBorderWidth = typedArray.getDimension(R.styleable.materialStatusProgressAttr_progressBarBorderWidth,

getResources().getDimension(R.dimen.material_status_progress_border));

typedArray.recycle();

mPaint = new Paint();

mPaint.setColor(arcColor);

mPaint.setStrokeWidth(mBorderWidth);

mPaint.setAntiAlias(true);

mPaint.setStyle(Paint.Style.STROKE);

arcRectF = new RectF();

mTickAnimation = new TickAnimation();

mTickAnimation.setDuration(800);

//对勾动画监听

mTickAnimation.setAnimationListener(new Animation.AnimationListener() {

@Override

public void onAnimationStart(Animation animation) {

}

@Override

public void onAnimationEnd(Animation animation) {

//当对勾动画完成后,延迟一秒回掉,不然动画效果不明显

if (mListener != null) {

postDelayed(new Runnable() {

@Override

public void run() {

mListener.onPasswordCorrectly();

}

}, 1000);

}

}

@Override

public void onAnimationRepeat(Animation animation) {

}

});

}

private void arcPaint() {

mPaint.reset();

mPaint.setColor(arcColor);

mPaint.setStrokeWidth(mBorderWidth);

mPaint.setAntiAlias(true);

mPaint.setStyle(Paint.Style.STROKE);

}

private void linePaint() {

mPaint.reset();

mPaint.setColor(arcColor);

mPaint.setStrokeWidth(mBorderWidth);

mPaint.setAntiAlias(true);

}

//对勾动画完成回调

public void setOnPasswordCorrectlyListener(OnPasswordCorrectlyListener listener) {

this.mListener = listener;

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

startY1 = getMeasuredHeight() / 2;

mRadius = getMeasuredHeight() / 2 - 2 * mBorderWidth;

startX1 = startY1 - getMeasuredHeight() / 5;

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

arcPaint();

canvas.drawArc(arcRectF, startAngle + incrementAngele, sweepAngle, false, mPaint);

if (animatorSet == null || !animatorSet.isRunning() && !isNeedTick) {

startAnimation();

}

if (isNeedTick) {

//补全圆

arcPaint();

canvas.drawArc(arcRectF, startAngle + incrementAngele + sweepAngle, 360 - sweepAngle, false, mPaint);

linePaint();

//画第一根线

canvas.drawLine(startX1, startY1, stopX1, stopY1, mPaint);

if (isAnimationOverHalf) {

//-2 +2 是为了两根线尽可能靠拢

canvas.drawLine(stopX1 - 2, stopY1 + 2, stopX2, stopY2, mPaint);

}

}

}

//对勾动画

private class TickAnimation extends Animation {

@Override

protected void applyTransformation(final float interpolatedTime, Transformation t) {

super.applyTransformation(interpolatedTime, t);

if (interpolatedTime <= 0.5f) {

stopX1 = startX1 + mRadius / 3 * interpolatedTime * 2;

stopY1 = startY1 + mRadius / 3 * interpolatedTime * 2;

isAnimationOverHalf = false;

} else {

stopX2 = stopX1 + (mRadius - 20) * (interpolatedTime - 0.5f) * 2;

stopY2 = stopY1 - (mRadius - 20) * (interpolatedTime - 0.5f) * 2;

isAnimationOverHalf = true;

}

invalidate();

}

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

mResize = (w < h) ? w : h;

setBound();

}

private void setBound() {

int paddingLeft = getPaddingLeft();

int paddingTop = getPaddingTop();

arcRectF.set(paddingLeft + mBorderWidth, paddingTop + mBorderWidth, mResize - paddingLeft - mBorderWidth, mResize - paddingTop - mBorderWidth);

}

public void startAnimation() {

isNeedTick = false;

if (animatorSet != null && animatorSet.isRunning()) {

animatorSet.cancel();

}

if (animatorSet == null) {

animatorSet = new AnimatorSet();

}

AnimatorSet set = loopAnimator();

animatorSet.play(set);

animatorSet.addListener(new AnimatorListener() {

private boolean isCancel = false;

@Override

public void onAnimationStart(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

@Override

public void onAnimationEnd(Animator animation) {

if (!isCancel) {

startAnimation();

}

}

@Override

public void onAnimationCancel(Animator animation) {

isCancel = true;

}

});

animatorSet.start();

}

/**

* 进度条旋转的动画

*/

private AnimatorSet loopAnimator() {

//从小圈到大圈

ValueAnimator holdAnimator1 = ValueAnimator.ofFloat(incrementAngele + DEFAULT_MIN_ANGLE, incrementAngele + 115f);

holdAnimator1.addUpdateListener(new AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

incrementAngele = (float) animation.getAnimatedValue();

}

});

holdAnimator1.setDuration(DEFAULT_DURATION);

holdAnimator1.setInterpolator(new LinearInterpolator());

ValueAnimator expandAnimator = ValueAnimator.ofFloat(DEFAULT_MIN_ANGLE, DEFAULT_MAX_ANGLE);

expandAnimator.addUpdateListener(new AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

sweepAngle = (float) animation.getAnimatedValue();

incrementAngele -= sweepAngle;

invalidate();

}

});

expandAnimator.setDuration(DEFAULT_DURATION);

expandAnimator.setInterpolator(new DecelerateInterpolator(2));

//从大圈到小圈

ValueAnimator holdAnimator = ValueAnimator.ofFloat(startAngle, startAngle + 115f);

holdAnimator.addUpdateListener(new AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

startAngle = (float) animation.getAnimatedValue();

}

});

holdAnimator.setDuration(DEFAULT_DURATION);

holdAnimator.setInterpolator(new LinearInterpolator());

ValueAnimator narrowAnimator = ValueAnimator.ofFloat(DEFAULT_MAX_ANGLE, DEFAULT_MIN_ANGLE);

narrowAnimator.addUpdateListener(new AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

sweepAngle = (float) animation.getAnimatedValue();

invalidate();

}

});

narrowAnimator.setDuration(DEFAULT_DURATION);

narrowAnimator.setInterpolator(new DecelerateInterpolator(2));

AnimatorSet set = new AnimatorSet();

set.play(holdAnimator1).with(expandAnimator);

set.play(holdAnimator).with(narrowAnimator).after(holdAnimator1);

return set;

}

//清除动画

private void cancelAnimator() {

if (animatorSet != null) {

animatorSet.cancel();

isNeedTick = true;

}

}

public void setSuccessfullyStatus() {

if (animatorSet != null) {

animatorSet.cancel();

isNeedTick = true;

startAnimation(mTickAnimation);

}

}

//重新setVisibility方法,当View不可见停止动画,及时释放资源。

@Override

public void setVisibility(int visibility) {

switch (visibility) {

case View.VISIBLE:

startAnimation();

break;

case View.INVISIBLE:

cancelAnimator();

break;

case View.GONE:

cancelAnimator();

break;

default:

break;

}

super.setVisibility(visibility);

}

public void setBorderWidth(int width) {

this.mBorderWidth = width;

}

public void setArcColor(int color) {

this.arcColor = color;

}

public interface OnPasswordCorrectlyListener {

void onPasswordCorrectly();

}

}

仿支付宝密码输入对话框

所有自定义控件完成,最后就是组装的过程。本文采用DialogFragment进行封装。同时,提供各个点击事件的回调。

public interface Callback {

//忘记密码

void onForgetPassword();

//密码输入完成,比如密码长度为六位,当密码输入六位时候,直接 发出密码校验请求

void onPasswordCompleted(CharSequence password);

//密码输入正确

void onPasswordCorrectly();

//取消弹出框

void onCancel();

}

看源码

public class PasswordKeypad extends DialogFragment implements View.OnClickListener, PasswordKeyboard.OnPasswordInputListener,

MDProgressBar.OnPasswordCorrectlyListener {

private TextView errorMsgTv;

private Callback mCallback;

private RelativeLayout passwordContainer;

private MDProgressBar progressBar;

private PasswordView passwordView;

private int passwordCount;

private boolean passwordState = true;

PasswordKeyboard numberKeyBoard;

private StringBuffer mPasswordBuffer = new StringBuffer();

@Override

public void onAttach(Activity context) {

super.onAttach(context);

if (context instanceof Callback) {

mCallback = (Callback) context;

}

}

@Nullable

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

return inflater.inflate(R.layout.password_keypad, container, false);

}

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setStyle(DialogFragment.STYLE_NO_TITLE, 0);

}

@Override

public void onStart() {

super.onStart();

DisplayMetrics dm = new DisplayMetrics();

getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);

Window window = getDialog().getWindow();

//去掉边框

window.setBackgroundDrawable(new ColorDrawable(0xffffffff));

window.setLayout(dm.widthPixels, window.getAttributes().height);

window.setWindowAnimations(R.style.exist_menu_animstyle);

window.setGravity(Gravity.BOTTOM);

}

@Override

public void onViewCreated(View view, Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

errorMsgTv = (TextView) view.findViewById(R.id.error_msg);

TextView forgetPasswordTv = (TextView) view.findViewById(R.id.forget_password);

TextView cancelTv = (TextView) view.findViewById(R.id.cancel_dialog);

passwordContainer = (RelativeLayout) view.findViewById(R.id.password_content);

progressBar = (MDProgressBar) view.findViewById(R.id.password_progressBar);

progressBar.setOnPasswordCorrectlyListener(this);

passwordView = (PasswordView) view.findViewById(R.id.password_inputBox);

//设置密码长度

if (passwordCount > 0) {

passwordView.setPasswordCount(passwordCount);

}

numberKeyBoard = (PasswordKeyboard) view.findViewById(R.id.password_keyboard);

numberKeyBoard.setOnPasswordInputListener(this);

cancelTv.setOnClickListener(this);

forgetPasswordTv.setOnClickListener(this);

}

/**

* 设置密码长度

*/

public void setPasswordCount(int passwordCount) {

this.passwordCount = passwordCount;

}

@Override

public void onClick(View v) {

if (R.id.cancel_dialog == v.getId()) {

if (mCallback != null) {

mCallback.onCancel();

}

dismiss();

} else if (R.id.forget_password == v.getId()) {

if (mCallback != null) {

mCallback.onForgetPassword();

}

}

}

public void setCallback(Callback callBack) {

this.mCallback = callBack;

}

public void setPasswordState(boolean correct) {

setPasswordState(correct, "");

}

public void setPasswordState(boolean correct, String msg) {

passwordState = correct;

if (correct) {

progressBar.setSuccessfullyStatus();

} else {

numberKeyBoard.resetKeyboard();

passwordView.clearPassword();

progressBar.setVisibility(View.GONE);

passwordContainer.setVisibility(View.VISIBLE);

errorMsgTv.setText(msg);

}

}

@Override

public void onPasswordCorrectly() {

if (mCallback != null) {

mCallback.onPasswordCorrectly();

}

}

//开始进度条旋转

private void startLoading(CharSequence password) {

passwordContainer.setVisibility(View.INVISIBLE);

progressBar.setVisibility(View.VISIBLE);

if (mCallback != null) {

mCallback.onPasswordCompleted(password);

}

}

@Override

public void onInput(String character) {

if (PasswordKeyboard.DEL.equals(character)) {

if (mPasswordBuffer.length() > 0) {

mPasswordBuffer.delete(mPasswordBuffer.length() - 1, mPasswordBuffer.length());

}

} else if (PasswordKeyboard.DONE.equals(character)) {

dismiss();

} else {

//密码输入错误状态,再次输入清除错误提示文字

if (!passwordState) {

if (!TextUtils.isEmpty(errorMsgTv.getText())) {

errorMsgTv.setText("");

}

}

mPasswordBuffer.append(character);

}

passwordView.setPassword(mPasswordBuffer);

if (mPasswordBuffer.length() == passwordView.getPasswordCount()) {

startLoading(mPasswordBuffer);

}

}

//密码对话框消失,清除密码输入

@Override

public void onDismiss(DialogInterface dialog) {

super.onDismiss(dialog);

if (mPasswordBuffer.length() > 0) {

mPasswordBuffer.delete(0, mPasswordBuffer.length());

}

}

}

如果本文对你有帮助,请不吝啬你的喜欢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值