VerificationCodeView 驗證碼自定義控制元件原始碼解析
阿新 • • 發佈:2019-01-06
https://github.com/JackTuoTuo/VerificationCodeView
喜歡該控制元件請給作者start.
演示
原理
在佈局中檔案中使用了一個透明的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