Android:仿支付宝交易密码框
App需要支付场景的时候,都会让用户输入密码交易框,如果用系统或者第三方键盘可能导致密码泄露。因此,比较多的App会自定义密码输入键盘来提供安全性。本文依照支付宝密码输入界面来设计,同时提供随机键盘功能。
项目地址:Github
看效果图
密码输入框效果图模拟器上,会导致线条绘制模糊。真机运行效果更棒。
数字密码键盘安全级别
低:系统提供键盘或者第三方输入法键盘。
中:自定义键盘,但不随机键盘数字。
高:自定义键盘,且随机键盘数字。
数字密码键盘实现原理
键盘实现的原理大致有两种方法。
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<Button> 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<String> 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<String> 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<String> 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<String> 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());
}
}
}
如果本文对你有帮助,请不吝啬你的喜欢。