先上两张效果图:
1.java类:
package com...ui; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.text.Editable; import android.text.InputFilter; import android.text.InputType; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import com...R; import java.lang.reflect.Field; /** * @ClassName: VerificationCodeView * @Desciption: //自定义验证码输入框view * @author: jesse_android * @date: 2018-03-29 */ public class VerificationCodeView extends LinearLayout implements TextWatcher, View.OnKeyListener, View.OnFocusChangeListener { private Context mContext; private long endTime = 0; private OnCodeFinishListener onCodeFinishListener; /** * 输入框数量 */ private int mEtNumber; /** * 输入框类型 */ private VCInputType mEtInputType; /** * 输入框的宽度 */ private int mEtWidth; /** * 输入框的高度 */ private int mEtHeight; /** * 文字颜色 */ private int mEtTextColor; /** * 文字大小 */ private float mEtTextSize; /** * 输入框背景 */ private int mEtTextBg; private int mCursorDrawable; public OnCodeFinishListener getOnCodeFinishListener() { return onCodeFinishListener; } public void setOnCodeFinishListener(OnCodeFinishListener onCodeFinishListener) { this.onCodeFinishListener = onCodeFinishListener; } public int getmEtNumber() { return mEtNumber; } public void setmEtNumber(int mEtNumber) { this.mEtNumber = mEtNumber; } public VCInputType getmEtInputType() { return mEtInputType; } public void setmEtInputType(VCInputType mEtInputType) { this.mEtInputType = mEtInputType; } public int getmEtWidth() { return mEtWidth; } public void setmEtWidth(int mEtWidth) { this.mEtWidth = mEtWidth; } public int getmEtHeight() { return mEtHeight; } public void setmEtHeight(int mEtHeight) { this.mEtHeight = mEtHeight; } public int getmEtTextColor() { return mEtTextColor; } public void setmEtTextColor(int mEtTextColor) { this.mEtTextColor = mEtTextColor; } public float getmEtTextSize() { return mEtTextSize; } public void setmEtTextSize(float mEtTextSize) { this.mEtTextSize = mEtTextSize; } public int getmEtTextBg() { return mEtTextBg; } public void setmEtTextBg(int mEtTextBg) { this.mEtTextBg = mEtTextBg; } public int getmCursorDrawable() { return mCursorDrawable; } public void setmCursorDrawable(int mCursorDrawable) { this.mCursorDrawable = mCursorDrawable; } public enum VCInputType { NUMBER, NUMBERPASSWORD, TEXT, TEXTPASSWORD, } public VerificationCodeView(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; @SuppressLint({"Recycle", "CustomViewStyleable"}) TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.vericationCodeView); mEtNumber = typedArray.getInteger(R.styleable.vericationCodeView_vcv_et_number, 4); int inputType = typedArray.getInt(R.styleable.vericationCodeView_vcv_et_inputType, VCInputType.NUMBER.ordinal()); mEtInputType = VCInputType.values()[inputType]; mEtWidth = typedArray.getDimensionPixelSize(R.styleable.vericationCodeView_vcv_et_width, 120); mEtHeight = typedArray.getDimensionPixelSize(R.styleable.vericationCodeView_vcv_et_height, 120); mEtTextColor = typedArray.getColor(R.styleable.vericationCodeView_vcv_et_text_color, Color.BLACK); mEtTextSize = typedArray.getDimensionPixelSize(R.styleable.vericationCodeView_vcv_et_text_size, 16); mEtTextBg = typedArray.getResourceId(R.styleable.vericationCodeView_vcv_et_bg, R.drawable.bg_et_input_veri_code); mCursorDrawable = typedArray.getResourceId(R.styleable.vericationCodeView_vcv_et_cursor,R.drawable.bg_et_cursor); //释放资源 typedArray.recycle(); initView(); } @SuppressLint("ResourceAsColor") private void initView() { for (int i = 0; i < mEtNumber; i++) { EditText editText = new EditText(mContext); initEditText(editText, i); addView(editText); if (i == 0) { //设置第一个editText获取焦点,并弹出软键盘 editText.setFocusable(true); if(mContext instanceof Activity){ ((Activity)mContext).getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); } } } } private void initEditText(EditText editText, int i) { int childHPadding = 0; int childVPadding = 0; LayoutParams layoutParams = new LayoutParams(mEtWidth,mEtHeight); layoutParams.bottomMargin = childVPadding; layoutParams.topMargin = childVPadding; layoutParams.leftMargin = childHPadding; layoutParams.rightMargin = childHPadding; layoutParams.gravity = Gravity.CENTER; editText.setLayoutParams(layoutParams); editText.setGravity(Gravity.CENTER); editText.setId(i); editText.setCursorVisible(true); editText.setMaxEms(1); editText.setTextColor(mEtTextColor); editText.setTextSize(mEtTextSize); editText.setMaxLines(1); editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(1)}); switch (mEtInputType) { case NUMBER: editText.setInputType(InputType.TYPE_CLASS_NUMBER); break; case NUMBERPASSWORD: editText.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_NUMBER_VARIATION_PASSWORD); break; case TEXT: editText.setInputType(InputType.TYPE_CLASS_TEXT); break; case TEXTPASSWORD: editText.setInputType(InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD); break; default: editText.setInputType(InputType.TYPE_CLASS_NUMBER); } editText.setPadding(0, 0, 0, 0); editText.setOnKeyListener(this); if(mEtTextBg != 0) { editText.setBackgroundResource(mEtTextBg); } //修改光标的颜色(反射) try { if(mCursorDrawable != 0) { Field f = TextView.class.getDeclaredField("mCursorDrawableRes"); f.setAccessible(true); f.set(editText, mCursorDrawable); } } catch (Exception ignored) { } editText.addTextChangedListener(this); editText.setOnFocusChangeListener(this); editText.setOnKeyListener(this); } @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 s) { if (s.length() != 0) { focus(); } } @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_DEL) { backFocus(); } return false; } @Override public void setEnabled(boolean enabled) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); child.setEnabled(enabled); } } /** * 获取焦点 */ private void focus() { int count = getChildCount(); EditText editText; //利用for循环找出还最前面那个还没被输入字符的EditText,并把焦点移交给它。 for (int i = 0; i < count; i++) { editText = (EditText) getChildAt(i); if (editText.getText().length() < 1) { editText.setCursorVisible(true); editText.requestFocus(); return; } else { editText.setCursorVisible(false); } } //如果最后一个输入框有字符,则返回结果 EditText lastEditText = (EditText) getChildAt(mEtNumber - 1); if (lastEditText.getText().length() > 0) { getResult(); } } private void backFocus() { //博主手机不好,经常点一次却触发两次`onKey`事件,就设置了一个防止多点击,间隔100毫秒。 long startTime = System.currentTimeMillis(); EditText editText; //循环检测有字符的`editText`,把其置空,并获取焦点。 for (int i = mEtNumber - 1; i >= 0; i--) { editText = (EditText) getChildAt(i); if (editText.getText().length() >= 1 && startTime - endTime > 100) { editText.setText(""); editText.setCursorVisible(true); editText.requestFocus(); endTime = startTime; return; } } } private void getResult() { StringBuffer stringBuffer = new StringBuffer(); EditText editText; for (int i = 0; i < mEtNumber; i++) { editText = (EditText) getChildAt(i); stringBuffer.append(editText.getText()); } if (onCodeFinishListener != null) { onCodeFinishListener.onComplete(stringBuffer.toString()); } } @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { focus(); } } public interface OnCodeFinishListener { void onComplete(String content); } }
2.values-attrs.xml中的定义:
<!-- 自定义验证码输入框--> <declare-styleable name="vericationCodeView"> <!--输入框的数量--> <attr name="vcv_et_number" format="integer" /> <!--输入类型--> <attr name="vcv_et_inputType"> <enum name="number" value="0" /> <enum name="numberPassword" value="1" /> <enum name="text" value="2" /> <enum name="textPassword" value="3" /> </attr> <!--输入框的宽度--> <attr name="vcv_et_width" format="dimension|reference" /> <!--输入框的高度--> <attr name="vcv_et_height" format="dimension|reference" /> <!--输入框文字颜色--> <attr name="vcv_et_text_color" format="color|reference" /> <!--输入框文字大小--> <attr name="vcv_et_text_size" format="dimension|reference" /> <!--输入框背景--> <attr name="vcv_et_bg" format="reference" /> <!--光标样式--> <attr name="vcv_et_cursor" format="reference" /> </declare-styleable>
3.使用:
xml:
<com.shushan.ui.VerificationCodeView android:id="@+id/verification_code_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/rl_already_send_to_phone" android:layout_centerHorizontal="true" android:layout_marginTop="29dp" app:vcv_et_width = "60.5dp" app:vcv_et_height = "56.5dp" />
Java:
private VerificationCodeView mVeriCodeView; mVeriCodeView = (VerificationCodeView) findViewById(R.id.verification_code_view); mVeriCodeView.setOnCodeFinishListener(this);//验证码输入完毕的回调
可自定义哪些部分看attrs.xml。
使用过程中遇到问题给我留言,会尽快回复。
欢迎参观博主的其他博客。