Android 自定义 View 实现密码输入控件
本示例介绍了一种 Android 自定义 View 实现输入控件的方案。不同于网络上大部分由多个 EditText 组合而成的输入控件,本例采用以下结构:
- 单个 EditText 作为输入的捕捉控件
- 多个 ImageView 的子类(TextImageView)作为显示的控件,用于绘制 EditText 中的数据
主要功能特性包括:
- 输入前和输入后输入框发生响应改变
- 点击自定义控件弹出软键盘
- EditText 数据捕捉,且 EditView 不可直接操作(防止数据处理混乱)
- 输入完成后得到相应提示
- TextImageView 子类可设置宽高、字体大小颜色以及两个 View 之间的间隔
TextImageView 实现
TextImageView 是 ImageView 的子类,其 onDraw 实现主要判断 text 长度是否大于 0,若大于 0 则绘制文字,同时处理字体颜色、字体大写及获取字体宽高细节。
@Override
protected void onDraw(Canvas canvas) {
if (text.length() > 0) {
if (isDrawSrc) {
super.onDraw(canvas);
}
canvas.drawText(text, 0, text.length(), (getMeasuredWidth() - textWidth) / 2, (getMeasuredHeight() + dy) / 2, textPaint);
} else {
super.onDraw(canvas);
}
}
PasswordView 实现
PasswordView 是一个自定义 ViewGroup,引入了一个布局,布局中包含一个 EditText(数据捕捉)和一个 LinearLayout(动态添加 TextImageView)。EditText 的宽高设置为 1dp 和 0dp(避免用户直接操作 EditText);LinearLayout 设置 divider 属性以控制 TextImageView 的间隔。
核心代码
控制 EditText 获取输入:
public void requestEtFocus() {
catchInput.setFocusable(true);
catchInput.setFocusableInTouchMode(true);
catchInput.setClickable(true);
catchInput.requestFocus();
showSoftKeyboard(catchInput);
catchInput.setCursorVisible(false);
catchInput.setSelection(catchInput.length());
}
动态添加 TextImageView 及输入监听:
// 动态添加 TextImageView
for (int i = 0; i < passwordLength; i++) {
TextImageView view = new TextImageView(context);
view.setTextSize(textSize);
view.setTextColor(textColor);
content.addView(view);
if (unInputBg != 0) {
view.setBackgroundResource(unInputBg); // 设置未输入前的背景
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) itemWidth, (int) itemHeight);
if (i == 0) {
params.setMargins((int) dpToPixel(1), 0, 0, 0);
}
if (i == passwordLength - 1) {
params.setMargins(0, 0, (int) dpToPixel(1), 0);
}
view.setLayoutParams(params);
views[i] = view;
// 分割字体,给 TextImageView 绘制文字
if (text != null && i < text.length()) {
setItemText(text.subSequence(i, i + 1));
}
}
// 输入监听
catchInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.length() > 0) {
// index:成员变量,保存当前的输入了几个字符
if (index < s.length()) {
removeItemText(); // 删除
} else {
setText(s);
if (s.length() == passwordLength) {
if (listener != null) {
// 输入完成回调
listener.onInputCodeEnd(s);
}
}
}
} else if (s.length() == 0 && index > 0) {
removeItemText();
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
实现比较简单,大多是一些细节处理,具体参考源码 PasswordView。

