演示
原理
在布局中文件中使用了一个透明的EditText来接受用户的输入事件, 在布局文件的LinearLayout中动态添加正方形输入框,正方形输入框其实是一个个的TextView。
初始化逻辑
- 初始化UI
// 初始UI
private void initUI() {
//创建TextView数组
initTextViews(getContext(), mEtNumber, mEtWidth, mEtDividerDrawable, mEtTextSize, mEtTextColor);
//将TextView控件加入进容器中
initEtContainer(mTextViews);
//设置监听
setListener();
}
- initTextViews()方法,该方法主要是创建一个TextView数组,存储需要得TextView
//初始化TextView
private void initTextViews(Context context, int etNumber, int etWidth, Drawable etDividerDrawable, float etTextSize, int etTextColor) {
et.setCursorVisible(false);//将光标隐藏
et.setFilters(new InputFilter[]{new InputFilter.LengthFilter(etNumber)}); //最大输入长度
// 设置分割线的宽度
if (etDividerDrawable != null) {
etDividerDrawable.setBounds(0, 0, etDividerDrawable.getMinimumWidth(), etDividerDrawable.getMinimumHeight());
containerEt.setDividerDrawable(etDividerDrawable);
}
mTextViews = new TextView[etNumber]; //创建一个储存输入的数据 TextView 控件数组
for (int i = 0; i < mTextViews.length; i++) {
TextView textView = new TextView(context);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, etTextSize);
textView.setTextColor(etTextColor);
textView.setWidth(etWidth);
textView.setHeight(etWidth);
if (i == 0) {
textView.setBackgroundDrawable(mEtBackgroundDrawableFocus);
} else {
textView.setBackgroundDrawable(mEtBackgroundDrawableNormal);
}
textView.setGravity(Gravity.CENTER);
textView.setFocusable(false);
mTextViews[i] = textView;
}
//若没有设置明文密文按钮背景图则明文密文按钮隐藏
if (mEtToggleIconOpen != null && mEtToggleIconClose != null) {
this.findViewById(R.id.btn_watch_paw).setVisibility(VISIBLE);
this.findViewById(R.id.btn_watch_paw).setBackgroundDrawable(mEtToggleIconClose);
}
}
- initEtContainer()方法,该方法是将创建好的TextView加入进LinerLayout容器中
private void initEtContainer(TextView[] mTextViews) {
for (TextView mTextView : mTextViews) {
//LinearLayout容器,存储TextView对象
containerEt.addView(mTextView);
}
}
- setListener()为控件设置监听事件
private void setListener() {
// 监听输入内容
et.addTextChangedListener(myTextWatcher);
// 监听删除按键
et.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
onKeyDelete();
return true;
}
return false;
}
});
//监听明文密文切换展示按钮
this.findViewById(R.id.btn_watch_paw).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mEtToggleIconOpen != null && mEtToggleIconClose != null) {
if (!mEtPwd) {
//当VerificationCodeView 中最后一个TextView也写入了数据,明文和密码切换按钮才生效
if (!TextUtils.isEmpty(mTextViews[mEtNumber - 1].getText().toString().trim())) {
showCiphertext();
mEtPwd = true;
}
} else {
if (!TextUtils.isEmpty(mTextViews[mEtNumber - 1].getText().toString().trim())) {
showPlaintext();
mEtPwd = false;
}
}
}
}
});
}
控件监听逻辑
- EditText设置输入监听,接受用户输入的内容,et.addTextChangedListener(myTextWatcher);
private class MyTextWatcher implements 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) {
}
@Override
public void afterTextChanged(Editable editable) {
String inputStr = editable.toString();
if (!TextUtils.isEmpty(inputStr)) {
//这里考虑到输入多个内容的情况,切割之后遍历放入TextView中
String[] strArray = inputStr.split("");
for (int i = 0; i < strArray.length; i++) {
// 不能大于输入框个数
if (i > mEtNumber) {
break;
}
//将输入的内容放入TextView中
setText(strArray[i]);
et.setText("");
}
}
}
}
// 给TextView 设置文字
private void setText(String inputContent) {
for (int i = 0; i < mTextViews.length; i++) {
final TextView tv = mTextViews[i];
if (tv.getText().toString().trim().equals("")) {
tv.setText(inputContent);
if (mEtPwd) {
mRefreshHandler.postDelayed(new Runnable() {// 将改变TextView属性为展示密文的属性,我改动的
@Override
public void run() {
//等待200毫秒,将TextView内容显示模式设置为密文
tv.setTransformationMethod(new CustomPasswordTransformationMethod(mEtPwdMode));
}
}, 200);
} else {
mRefreshHandler.postDelayed(new Runnable() {
@Override
public void run() {
//马上将TextView内容显示模式设置为明文
tv.setTransformationMethod(SingleLineTransformationMethod.getInstance());
}
}, 0);
}
// 添加输入完成的监听
if (inputCompleteListener != null) {
//对外暴露输入监听事件
inputCompleteListener.inputComplete();
}
tv.setBackgroundDrawable(mEtBackgroundDrawableNormal);
if (i < mEtNumber - 1) {
mTextViews[i + 1].setBackgroundDrawable(mEtBackgroundDrawableFocus);
}
break;
}
}
//当输入内容显示到最后一个TextView时候,若用户设置了明文密文切换按钮的背景,则将按钮显示并且设置相应的背景。
if (!TextUtils.isEmpty(mTextViews[mEtNumber - 1].getText().toString())) {
if (mEtToggleIconOpen != null && mEtToggleIconClose != null) {
this.findViewById(R.id.btn_watch_paw).setVisibility(VISIBLE);
this.findViewById(R.id.btn_watch_paw).setBackgroundDrawable(mEtToggleIconOpen);
}
}
}
- EditText设置删除监听
// 监听删除
private void onKeyDelete() {
for (int i = mTextViews.length - 1; i >= 0; i--) {
TextView tv = mTextViews[i];
if (!tv.getText().toString().trim().equals("")) {
//如果该TextView内容不为空,就将TextView设置为空
tv.setText("");
// 添加删除完成监听
if (inputCompleteListener != null) {
//对外暴露删除监听事件
inputCompleteListener.deleteContent();
}
tv.setBackgroundDrawable(mEtBackgroundDrawableFocus);
if (i < mEtNumber - 1) {
mTextViews[i + 1].setBackgroundDrawable(mEtBackgroundDrawableNormal);
}
break;
}
}
//当最后一个TextView内容为空,若用户设置了明文密文切换按钮的背景,则将按钮显示并且设置相应的背景。
if (TextUtils.isEmpty(mTextViews[mEtNumber - 1].getText().toString())) {
if (mEtToggleIconOpen != null && mEtToggleIconClose != null) {
this.findViewById(R.id.btn_watch_paw).setVisibility(VISIBLE);
this.findViewById(R.id.btn_watch_paw).setBackgroundDrawable(mEtToggleIconClose);
}
}
}
- 为明文密文切换按钮设置监听事件
//监听明文密文切换展示按钮
this.findViewById(R.id.btn_watch_paw).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mEtToggleIconOpen != null && mEtToggleIconClose != null) {
if (!mEtPwd) {
//当VerificationCodeView 中最后一个TextView也写入了数据,明文和密码切换按钮才生效
if (!TextUtils.isEmpty(mTextViews[mEtNumber - 1].getText().toString().trim())) {
showCiphertext();
mEtPwd = true;
}
} else {
if (!TextUtils.isEmpty(mTextViews[mEtNumber - 1].getText().toString().trim())) {
showPlaintext();
mEtPwd = false;
}
}
}
}
});
地址
- 该源码解读是基于1.0.6版本。
- GitHub