支付宝淘宝 - 自定义密码输入框和键盘
程序员文章站
2022-06-22 12:26:36
1.密码输入框 attrs.xml PasswordEditText 目前的效果就是点击之后会弹出系统的键盘,实现了基本的效果,接下来我们再加入监听也就说当密码输入完成我们需要回调监听。 2.自定义键盘: ui_customer_keyboard.xml: 1
1.密码输入框
attrs.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="PasswordEditText"> <!-- 密码的个数 --> <attr name="passwordNumber" format="integer"/> <!-- 密码圆点的半径 --> <attr name="passwordRadius" format="dimension" /> <!-- 密码圆点的颜色 --> <attr name="passwordColor" format="color" /> <!-- 分割线的颜色 --> <attr name="divisionLineColor" format="color" /> <!-- 分割线的大小 --> <attr name="divisionLineSize" format="color" /> <!-- 背景边框的颜色 --> <attr name="bgColor" format="color" /> <!-- 背景边框的大小 --> <attr name="bgSize" format="dimension" /> <!-- 背景边框的圆角大小 --> <attr name="bgCorner" format="dimension"/> </declare-styleable> </resources>
PasswordEditText
public class PasswordEditText extends EditText { // 画笔 private Paint mPaint; // 一个密码所占的宽度 private int mPasswordItemWidth; // 密码的个数默认为6位数 private int mPasswordNumber = 6; // 背景边框颜色 private int mBgColor = Color.parseColor("#d1d2d6"); // 背景边框大小 private int mBgSize = 1; // 背景边框圆角大小 private int mBgCorner = 0; // 分割线的颜色 private int mDivisionLineColor = mBgColor; // 分割线的大小 private int mDivisionLineSize = 1; // 密码圆点的颜色 private int mPasswordColor = mDivisionLineColor; // 密码圆点的半径大小 private int mPasswordRadius = 4; private PasswordFullListener mListener; public PasswordEditText(Context context) { this(context, null); } public PasswordEditText(Context context, AttributeSet attrs) { super(context, attrs); initPaint(); initAttributeSet(context, attrs); // 设置输入模式是密码 setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); // 不显示光标 setCursorVisible(false); } /** * 初始化属性 */ private void initAttributeSet(Context context, AttributeSet attrs) { TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PasswordEditText); // 获取大小 mDivisionLineSize = (int) array.getDimension(R.styleable.PasswordEditText_divisionLineSize, dip2px(mDivisionLineSize)); mPasswordRadius = (int) array.getDimension(R.styleable.PasswordEditText_passwordRadius, dip2px(mPasswordRadius)); mBgSize = (int) array.getDimension(R.styleable.PasswordEditText_bgSize, dip2px(mBgSize)); mBgCorner = (int) array.getDimension(R.styleable.PasswordEditText_bgCorner, 0); // 获取颜色 mBgColor = array.getColor(R.styleable.PasswordEditText_bgColor, mBgColor); mDivisionLineColor = array.getColor(R.styleable.PasswordEditText_divisionLineColor, mDivisionLineColor); mPasswordColor = array.getColor(R.styleable.PasswordEditText_passwordColor, mDivisionLineColor); array.recycle(); } /** * 初始化画笔 */ private void initPaint() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setDither(true); } /** * dip 转 px */ private int dip2px(int dip) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, getResources().getDisplayMetrics()); } @Override protected void onDraw(Canvas canvas) { int passwordWidth = getWidth() - (mPasswordNumber - 1) * mDivisionLineSize; mPasswordItemWidth = passwordWidth / mPasswordNumber; // 绘制背景 drawBg(canvas); // 绘制分割线 drawDivisionLine(canvas); // 绘制密码 drawHidePassword(canvas); } /** * 绘制背景 */ private void drawBg(Canvas canvas) { mPaint.setColor(mBgColor); // 设置画笔为空心 mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(mBgSize); RectF rectF = new RectF(mBgSize, mBgSize, getWidth() - mBgSize, getHeight() - mBgSize); // 如果没有设置圆角,就画矩形 if (mBgCorner == 0) { canvas.drawRect(rectF, mPaint); } else { // 如果有设置圆角就画圆矩形 canvas.drawRoundRect(rectF, mBgCorner, mBgCorner, mPaint); } } /** * 绘制隐藏的密码 */ private void drawHidePassword(Canvas canvas) { int passwordLength = getText().length(); mPaint.setColor(mPasswordColor); // 设置画笔为实心 mPaint.setStyle(Paint.Style.FILL); for (int i = 0; i < passwordLength; i++) { int cx = i * mDivisionLineSize + i * mPasswordItemWidth + mPasswordItemWidth / 2 + mBgSize; canvas.drawCircle(cx, getHeight() / 2, mPasswordRadius, mPaint); } // 判断密码是否填充完毕 if (passwordLength >= mPasswordNumber) { // 代表密码已经填充满了 if (mListener != null) { mListener.passwordFull(getText().toString().trim()); } } } /** * 绘制分割线 */ private void drawDivisionLine(Canvas canvas) { mPaint.setStrokeWidth(mDivisionLineSize); mPaint.setColor(mDivisionLineColor); for (int i = 0; i < mPasswordNumber - 1; i++) { int startX = (i + 1) * mDivisionLineSize + (i + 1) * mPasswordItemWidth + mBgSize; canvas.drawLine(startX, mBgSize, startX, getHeight() - mBgSize, mPaint); } } /** * 添加密码 */ public void addPassword(String number) { number = getText().toString().trim() + number; if (number.length() > mPasswordNumber) { return; } setText(number); } /** * 删除最后一位密码 */ public void deleteLastPassword() { String currentText = getText().toString().trim(); if (TextUtils.isEmpty(currentText)) { return; } currentText = currentText.substring(0, currentText.length() - 1); setText(currentText); } /** * 设置密码填充满的监听 */ public void setOnPasswordFullListener(PasswordFullListener listener) { this.mListener = listener; } /** * 密码已经全部填满 */ public interface PasswordFullListener { public void passwordFull(String password); } }
目前的效果就是点击之后会弹出系统的键盘,实现了基本的效果,接下来我们再加入监听也就说当密码输入完成我们需要回调监听。
2.自定义键盘:
ui_customer_keyboard.xml:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/activity_main" 4 android:layout_width="match_parent" 5 android:layout_height="wrap_content" 6 android:background="#EBEBEB" 7 android:orientation="vertical"> 8 9 <LinearLayout 10 android:layout_width="match_parent" 11 android:layout_height="wrap_content"> 12 13 <TextView 14 android:layout_width="0dp" 15 android:layout_height="wrap_content" 16 android:layout_marginRight="1dp" 17 android:layout_weight="1" 18 android:background="#FFFFFF" 19 android:gravity="center" 20 android:padding="20dp" 21 android:text="1" /> 22 23 <TextView 24 android:layout_width="0dp" 25 android:layout_height="wrap_content" 26 android:layout_marginRight="1dp" 27 android:layout_weight="1" 28 android:background="#FFFFFF" 29 android:gravity="center" 30 android:padding="20dp" 31 android:text="2" /> 32 33 <TextView 34 android:layout_width="0dp" 35 android:layout_height="wrap_content" 36 android:layout_weight="1" 37 android:background="#FFFFFF" 38 android:gravity="center" 39 android:padding="20dp" 40 android:text="3" /> 41 </LinearLayout> 42 43 <LinearLayout 44 android:layout_width="match_parent" 45 android:layout_height="wrap_content" 46 android:layout_marginTop="1dp"> 47 48 <TextView 49 android:layout_width="0dp" 50 android:layout_height="wrap_content" 51 android:layout_marginRight="1dp" 52 android:layout_weight="1" 53 android:background="#FFFFFF" 54 android:gravity="center" 55 android:padding="20dp" 56 android:text="4" /> 57 58 <TextView 59 android:layout_width="0dp" 60 android:layout_height="wrap_content" 61 android:layout_marginRight="1dp" 62 android:layout_weight="1" 63 android:background="#FFFFFF" 64 android:gravity="center" 65 android:padding="20dp" 66 android:text="5" /> 67 68 <TextView 69 android:layout_width="0dp" 70 android:layout_height="wrap_content" 71 android:layout_weight="1" 72 android:background="#FFFFFF" 73 android:gravity="center" 74 android:padding="20dp" 75 android:text="6" /> 76 </LinearLayout> 77 78 <LinearLayout 79 android:layout_width="match_parent" 80 android:layout_height="wrap_content" 81 android:layout_marginTop="1dp"> 82 83 <TextView 84 android:layout_width="0dp" 85 android:layout_height="wrap_content" 86 android:layout_marginRight="1dp" 87 android:layout_weight="1" 88 android:background="#FFFFFF" 89 android:gravity="center" 90 android:padding="20dp" 91 android:text="7" /> 92 93 <TextView 94 android:layout_width="0dp" 95 android:layout_height="wrap_content" 96 android:layout_marginRight="1dp" 97 android:layout_weight="1" 98 android:background="#FFFFFF" 99 android:gravity="center" 100 android:padding="20dp" 101 android:text="8" /> 102 103 <TextView 104 android:layout_width="0dp" 105 android:layout_height="wrap_content" 106 android:layout_weight="1" 107 android:background="#FFFFFF" 108 android:gravity="center" 109 android:padding="20dp" 110 android:text="9" /> 111 112 </LinearLayout> 113 114 <LinearLayout 115 android:layout_width="match_parent" 116 android:layout_height="wrap_content" 117 android:layout_marginTop="1dp" 118 android:orientation="horizontal"> 119 120 <TextView 121 android:layout_width="0dp" 122 android:layout_height="wrap_content" 123 android:layout_marginRight="1dp" 124 android:layout_weight="1" 125 android:gravity="center" 126 android:padding="20dp" /> 127 128 <TextView 129 android:layout_width="0dp" 130 android:layout_height="wrap_content" 131 android:layout_marginRight="1dp" 132 android:layout_weight="1" 133 android:background="#FFFFFF" 134 android:gravity="center" 135 android:padding="20dp" 136 android:text="0" /> 137 138 <ImageView 139 android:layout_width="0dp" 140 android:layout_height="wrap_content" 141 android:layout_weight="1" 142 android:gravity="center" 143 android:padding="15dp" 144 android:layout_gravity="center_vertical" 145 android:src="@drawable/customer_password_keyboard_delete" /> 146 </LinearLayout> 147 </LinearLayout>View Code
CustomerKeyboard.java:
1 public class CustomerKeyboard extends LinearLayout implements View.OnClickListener { 2 private CustomerKeyboardClickListener mListener; 3 4 public CustomerKeyboard(Context context) { 5 this(context, null); 6 } 7 8 public CustomerKeyboard(Context context, AttributeSet attrs) { 9 this(context, attrs, 0); 10 } 11 12 public CustomerKeyboard(Context context, AttributeSet attrs, int defStyleAttr) { 13 super(context, attrs, defStyleAttr); 14 inflate(context, R.layout.ui_customer_keyboard, this); 15 setChildViewOnclick(this); 16 } 17 18 /** 19 * 设置键盘子View的点击事件 20 */ 21 private void setChildViewOnclick(ViewGroup parent) { 22 int childCount = parent.getChildCount(); 23 for (int i = 0; i < childCount; i++) { 24 // 不断的递归设置点击事件 25 View view = parent.getChildAt(i); 26 if (view instanceof ViewGroup) { 27 setChildViewOnclick((ViewGroup) view); 28 continue; 29 } 30 view.setOnClickListener(this); 31 } 32 } 33 34 @Override 35 public void onClick(View v) { 36 View clickView = v; 37 if (clickView instanceof TextView) { 38 // 如果点击的是TextView 39 String number = ((TextView) clickView).getText().toString(); 40 if (!TextUtils.isEmpty(number)) { 41 if (mListener != null) { 42 // 回调 43 mListener.click(number); 44 } 45 } 46 } else if (clickView instanceof ImageView) { 47 // 如果是图片那肯定点击的是删除 48 if (mListener != null) { 49 mListener.delete(); 50 } 51 } 52 } 53 54 /** 55 * 设置键盘的点击回调监听 56 */ 57 public void setOnCustomerKeyboardClickListener(CustomerKeyboardClickListener listener) { 58 this.mListener = listener; 59 } 60 61 /** 62 * 点击键盘的回调监听 63 */ 64 public interface CustomerKeyboardClickListener { 65 public void click(String number); 66 public void delete(); 67 } 68 }
3.最后的测试
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#f7f7f7" android:orientation="vertical"> <com.hc.passwordedittext.PasswordEditText android:id="@+id/password_edit_text" android:layout_width="match_parent" android:layout_marginTop="23dp" android:layout_marginRight="45dp" android:background="@null" android:padding="10dp" app:bgCorner="3dp" android:layout_marginLeft="45dp" android:layout_height="wrap_content" /> <com.hc.passwordedittext.CustomerKeyboard android:id="@+id/custom_key_board" android:layout_marginTop="23dp" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
MainActivity.java
1 public class MainActivity extends Activity implements CustomerKeyboard.CustomerKeyboardClickListener, 2 PasswordEditText.PasswordFullListener{ 3 4 private CustomerKeyboard mCustomerKeyboard; 5 private PasswordEditText mPasswordEditText; 6 7 @Override 8 protected void onCreate(Bundle savedInstanceState) { 9 super.onCreate(savedInstanceState); 10 setContentView(R.layout.activity_main); 11 mPasswordEditText = (PasswordEditText) findViewById(R.id.password_et); 12 mCustomerKeyboard = (CustomerKeyboard) findViewById(R.id.custom_key_board); 13 // 设置监听 14 mCustomerKeyboard.setOnCustomerKeyboardClickListener(this); 15 mPasswordEditText.setOnPasswordFullListener(this); 16 } 17 18 /** 19 * 键盘数字点击监听回调方法 20 */ 21 @Override 22 public void click(String number) { 23 mPasswordEditText.addPassword(number); 24 } 25 26 /** 27 * 键盘删除点击监听回调方法 28 */ 29 @Override 30 public void delete() { 31 mPasswordEditText.deleteLastPassword(); 32 } 33 34 /** 35 * 密码输入完毕回调方法 36 */ 37 @Override 38 public void passwordFull(String password) { 39 Toast.makeText(this, "密码填充完毕:" + password, Toast.LENGTH_SHORT).show(); 40 } 41 }
https://github.com/HCDarren/PasswordEditText
上一篇: Java实现无头双向链表操作
下一篇: 【自问自答】关于 Swift 的几个疑问