自定义验证码输入框:VerificationCodeView

先上两张效果图:

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
以下是一个简单的HTML5表单,包含所有要求的元素: ```html <!DOCTYPE html> <html> <head> <title>注册表单</title> <meta charset="utf-8"> </head> <body> <form> <label for="email">邮件地址:</label> <input type="text" id="email" name="email" required> @ <input type="text" id="email-domain" name="email-domain" required> . <input type="text" id="email-tld" name="email-tld" required> <br><br> <label for="password">密码:</label> <input type="password" id="password" name="password" required> <br><br> <label for="confirm-password">确认密码:</label> <input type="password" id="confirm-password" name="confirm-password" required> <br><br> <label for="phone">手机号码:</label> <input type="tel" id="phone" name="phone" pattern="[0-9]{11}" required> <br><br> <label for="captcha">验证码:</label> <input type="text" id="captcha" name="captcha" required> <img src="captcha.php" alt="验证码" onclick="this.src='captcha.php?'+Math.random()"> <button type="button" onclick="this.previousSibling.src='captcha.php?'+Math.random()">换一张</button> <br><br> <button type="button">免费获取验证码</button> <label for="sms-code">短信验证码:</label> <input type="text" id="sms-code" name="sms-code" required> <br><br> <label for="terms">同意服务条款:</label> <input type="checkbox" id="terms" name="terms" required> <br><br> <button type="submit">立即注册</button> </form> </body> </html> ``` 解释一下: 1. 邮件地址的三个部分分别是`email`、`email-domain`和`email-tld`,都是必填项。 2. 密码输入框使用`type="password"`。 3. 确认密码输入框与密码输入框相同。 4. 手机号码输入框使用`type="tel"`和`pattern="[0-9]{11}"`,表示必须是11位数字。 5. 验证码使用一个`img`标签显示图片,并在`onclick`事件中生成新的验证码图片。 6. 免费获取验证码使用一个普通的`button`标签。 7. 短信验证输入框与其他输入框相同。 8. 同意服务条款使用一个`checkbox`标签,并添加`required`属性。 9. 立即注册使用一个普通的`button`标签,不是`type="submit"`,因为需要添加JavaScript代码来验证表单。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JesseAndroid

每一份支持都是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值