先上两张效果图:
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。
使用过程中遇到问题给我留言,会尽快回复。
欢迎参观博主的其他博客。
最后推荐给一些想进大厂或者还没有拿到心仪offer的攻城狮们一本书,由大厂java面试官胡书敏编写,满满的干货,助你进到想去的公司。
博主上传资源下载链接:
环状百分比显示视图源码:
https://download.csdn.net/download/yonghuming_jesse/10677919
自制免费无广告小说阅读APP下载:
https://download.csdn.net/download/yonghuming_jesse/10390364
全屏播放视频不拉伸源码:
https://download.csdn.net/download/yonghuming_jesse/10646274
科大讯飞语音评测服务接入源码:
https://download.csdn.net/download/yonghuming_jesse/10616924
android饺子播放器使用源码:
https://download.csdn.net/download/yonghuming_jesse/10619119
视频播放前显示视频第一帧源码:
https://download.csdn.net/download/yonghuming_jesse/10646332