Android 密码输入控件的另一种实现:单个 EditText 配上多个 ImageView
网上常见的密码输入组件大多用多个 EditText 拼起来,每个格一个输入框。但我这里用了另一种思路:只放一个 EditText 作为数据捕获器,再用多个 ImageView 的子类 TextImageView 来展示输入内容。这样 EditText 本身不用动,避免了焦点切换和输入混乱的麻烦。
功能上实现了这几点:
- 点击控件弹出软键盘,自动聚焦隐藏的 EditText。
- 输入前后方框背景切换。
- 输入完成触发回调。
- TextImageView 可以单独设置大小、文字颜色、间距等。
先看最底层的 TextImageView:
@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);
}
}
它在 onDraw 里判断如果有文字就绘制文字,否则正常绘制背景图。里面处理了文字居中和字体大小,细节就不展开了。
接着是 PasswordView,它是一个自定义 ViewGroup。布局里放了一个 EditText(宽高 1dp / 0dp,几乎看不见)和一个 LinearLayout(用来动态添加 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 ; i < passwordLength; i++) {
(context);
view.setTextSize(textSize);
view.setTextColor(textColor);
content.addView(view);
(unInputBg != ) {
view.setBackgroundResource(unInputBg);
}
LinearLayout. .LayoutParams(
() itemWidth, () itemHeight);
(i == ) {
params.setMargins(() dpToPixel(), , , );
}
(i == passwordLength - ) {
params.setMargins(, , () dpToPixel(), );
}
view.setLayoutParams(params);
views[i] = view;
(text != && i < text.length()) {
setItemText(text.subSequence(i, i + ));
}
}
catchInput.addTextChangedListener( () {
{}
{
(s.length() > ) {
(index < s.length()) {
removeItemText();
} {
setText(s);
(s.length() == passwordLength && listener != ) {
listener.onInputCodeEnd(s);
}
}
} (s.length() == && index > ) {
removeItemText();
}
}
{}
});


