在开发中发现在某些机型某些系统中输入密码时会发现密码会明文显示一会然后再显示成密文,如下图所示:
这是不安全的。这时需要自定义密码输入框,输入密码时直接就是密文显示。这个需要自定义属性。不了解自定义属性的可以先查查,这里只写步骤。
1、自定义输入框密码属性。在项目res-values目录下创建attrs.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PasswordEditText">
<!--密码圆点半径-->
<attr name="pwdRadius" format="dimension"></attr>
<!--密码圆点颜色-->
<attr name="pwdColor" format="color"></attr>
</declare-styleable>
</resources>
2、创建自定义控件。新建一个类继承EditText。
/**
* @author PanBM
* @description 自定义密码框
* @date 2019/6/27
*/
@SuppressLint("AppCompatCustomView")
public class PasswordEditText extends EditText {
private Paint paint;//画笔
private int pwdColor= Color.parseColor("#FFFFFF");//密码的颜色
private int pwdRadius=2;//密码圆点半径大小
private int pwdItemWidth;//一个密码所占的宽度
private int paddingLeft=14;//字符距离左边框的距离
private int pwdLength;//密码的长度
private int pwdMaxLength=10;//密码最长的长度
private long cursorFlashTime=500;//光标闪动间隔时间
private int cursorPosition;//光标位置
private int cursorWidth;//光标粗细
private int cursorHeight;//光标高度
private int cursorColor=Color.WHITE;//光标颜色
private boolean isCursorShowing=false;//光标是否正在显示
private boolean isCursorEnable=true;//是否开启光标
private CharSequence hint;
private int hintTextColor=Color.GRAY;
private Timer timer;
private TimerTask timerTask;
public PasswordEditText(Context context) {
super(context);
}
public PasswordEditText(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
//初始化属性
initAttributeSet(context,attrs);
//设置输入模式是密码
setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
//显示光标。不起作用,需要自己画光标
// setCursorVisible(true);
}
private void initAttributeSet(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PasswordEditText);
pwdRadius = (int) typedArray.getDimension(R.styleable.PasswordEditText_pwdRadius, dip2px(pwdRadius));
pwdColor = typedArray.getColor(R.styleable.PasswordEditText_pwdColor,pwdColor);
typedArray.recycle();
}
private void initPaint() {
paint=new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
timerTask=new TimerTask() {
@Override
public void run() {
isCursorShowing=!isCursorShowing;
postInvalidate();
}
};
timer=new Timer();
hint=getHint();//获取hint文字
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
timer.scheduleAtFixedRate(timerTask,0,cursorFlashTime);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
timer.cancel();
}
private int dip2px(int dip){
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dip,getResources().getDisplayMetrics());
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
}
@Override
protected void onDraw(Canvas canvas) {
pwdItemWidth=dip2px(7);
//绘制密码
drawPwd(canvas);
//绘制光标
drawCursor(canvas);
//绘制hint。xml页面引用自定义密码控件发现设置hint内容不显示,这个需要自己绘制才会显示。
drawHint(canvas);
}
private void drawHint(Canvas canvas) {
if(TextUtils.isEmpty(hint) || !TextUtils.isEmpty(getText())) {
return;
}
canvas.save();
paint.setColor(hintTextColor);
paint.setStyle(Paint.Style.FILL);
paint.setTextSize(dip2px(18));
Paint.FontMetricsInt fontMetricsInt=paint.getFontMetricsInt();
int baseLine=(getHeight()-fontMetricsInt.bottom+fontMetricsInt.top)/2-fontMetricsInt.top;
canvas.drawText(hint,0,hint.length(),getPaddingLeft(),
baseLine,
paint);
canvas.restore();
}
private void drawCursor(Canvas canvas) {
paint.setColor(cursorColor);
paint.setStrokeWidth(cursorWidth);
paint.setStyle(Paint.Style.FILL);
//光标未显示&&开启光标&&获得焦点
if(!isCursorShowing && isCursorEnable && hasFocus()) {
//起始点x=paddingLeft+单个密码占用宽度*光标下标
//起始点y=paddingTop+(单个密码占用宽度-光标高度)/2
//终止点x=起始点x
//终止点y=起始点y+光标高度
canvas.drawLine(getPaddingLeft()+pwdItemWidth*pwdLength,
getPaddingTop()+(pwdItemWidth-cursorHeight)/2+dip2px(7),
getPaddingLeft()+pwdItemWidth*pwdLength,
getPaddingTop()+cursorHeight,
paint);
}
}
private void drawPwd(Canvas canvas) {
pwdLength = getText().length();
// if(pwdLength>pwdMaxLength) {
// pwdLength=pwdMaxLength;
// }
paint.setColor(pwdColor);
//设置笔画为实心
paint.setStyle(Paint.Style.FILL);
for (int i=0;i<pwdLength;i++){
int cx=dip2px(paddingLeft)+i*pwdItemWidth+pwdItemWidth/2;
canvas.drawCircle(cx,getHeight()/2,pwdRadius,paint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//光标宽度
cursorWidth=dip2px(1);
//光标长度
cursorHeight=dip2px(23);
}
3、定义完成后在xml文件中引用该控件
<--控件路径-->
<com.example.view.PasswordEditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/et_username"
android:layout_centerInParent="true"
android:layout_marginLeft="@dimen/normal_match_40"
android:layout_marginRight="@dimen/normal_match_40"
android:layout_marginTop="@dimen/normal_padding"
android:background="@drawable/shape_et"
android:hint="@string/password"
android:ellipsize="end"
android:textColor="@color/white"
android:textSize="@dimen/text_size_18"/>
4、在activity页面调用就行了。
另外,有的时候输入密码时需要键盘数字和字母随机排列,这个有个第三方框架不错自定义加密键盘 支持输入乱序
参考文章:
Android 处理EditText光标显示在hint文字之前的问题