自定义手机验证码输入框

验证码输入框

类似上图,几个输入框横线排列。

具体思路:使用linearLayout 横向添加几个ediTtext,第一个首先获取焦点,当输入完成第一个,焦点依次传递到下一个,如果是键盘删除按钮删除,判断当前获取焦点的输入框是否有内容,如果有内容则清空内容,如果无内容那么焦点往前一个输入框传递。
关于动态修改光标的样式,EditText没有提供api,那么一想肯定就是反射了,代码如下:

/**
 * 改变光标的颜色
 * @param editText
 */
public void changeCursorDrawable(EditText editText) {
    try {
        Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
        f.setAccessible(true);
        f.set(editText, R.drawable.cursor_drawable);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

控件完整代码如下所示:

package com.job.android.views;

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.Nullable;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;


import com.job.android.R;

import java.lang.reflect.Field;

/**
 * Created by sima.tu on 2018/8/31.
 * 验证码的输入框
 */

public class VerificationCodeView extends LinearLayout implements TextWatcher, View.OnFocusChangeListener, View.OnKeyListener {
    private static String TAG = VerificationCodeView.class.getName();
    private Context mContext;
    private int codeViewNumber;                  // 子输入框的个数
    private int mTextSize;                       //输入框字体大小
    private int mTextColor;

    private int codeViewChildMarginRightAndLeft; // 子输入框的左右外间距

    private EditText mNexInputEditText;          //下一个即将获取焦点的输入框
    private EditText mCurrentFocusedEditText;    //当前占有焦点的输入框


    private InputCompleteListener inputCompleteListener;

    private int mFocusedIndex = -1;              //当前占有焦点的输入框的下标
    /**
     * @return
     */
    public EditText getmCurrentFocusedEditText() {
        return mCurrentFocusedEditText;
    }

    /**
     * 设置当前获取焦点的editText 方便外界设置
     *
     * @param mCurrentFocusedEditText
     */
    public void setmCurrentFocusedEditText(EditText mCurrentFocusedEditText) {
        this.mCurrentFocusedEditText = mCurrentFocusedEditText;
    }



    public void setInputCompleteListener(InputCompleteListener inputCompleteListener) {
        this.inputCompleteListener = inputCompleteListener;
    }

    public VerificationCodeView(Context context) {
        super(context);
    }

    public VerificationCodeView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        initView(context, attrs);

    }

    public VerificationCodeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        initView(context, attrs);
    }

    public void initView(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.VerificationCodeView);
        if (null == array) {
            return;
        }
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.VerificationCodeView_codeViewNumber:
                    codeViewNumber = array.getInt(attr, 6);
                    break;
                case R.styleable.VerificationCodeView_codeViewChildMarginRightAndLeft:
                    codeViewChildMarginRightAndLeft = (int) array.getDimension(attr,0);
                    break;
            }
        }
        array.recycle();

        for (int i = 0; i < codeViewNumber; i++) {
            EditText editText = new EditText(context);
            LayoutParams params = new LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
            params.setMargins(10, 0, 10, 0);
            params.weight = 1f;
            params.gravity = Gravity.CENTER;
            editText.setLayoutParams(params);

            editText.setGravity(Gravity.CENTER); // 设置让光标居中
            editText.setBackground(getResources().getDrawable(R.drawable.edittext_underline));
            editText.setBackgroundResource(R.drawable.edittext_underline); //  设置光标的下划线样式

            editText.setInputType(InputType.TYPE_CLASS_NUMBER);
            editText.setTextSize(30);
            editText.setTextColor(getResources().getColor(R.color.gray_444444));
            editText.addTextChangedListener(this);
            editText.setOnFocusChangeListener(this);
            editText.setOnKeyListener(this);
            if (i == 0) {
                mNexInputEditText = editText;
            }
            editText.setTag(i);
            addView(editText);
            //由于无法直接在代码中改变光标样式 这里使用反射去修改光标样式
            changeCursorDrawable(editText);
            if (i == 0) { //设置第一个editText获取焦点
                editText.requestFocus();
            }
        }
    }

    /**
     * 改变光标的颜色
     * @param editText
     */
    public void changeCursorDrawable(EditText editText) {
        try {
            Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
            f.setAccessible(true);
            f.set(editText, R.drawable.cursor_drawable);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (s.length() < 1) {
            return;
        }
        if (s.toString().length() > 1) {
            String string = s.toString().substring(0, 1);
            ((EditText) getChildAt(mFocusedIndex)).setText(string);
        }
    }

    @Override
    public void afterTextChanged(Editable s) {
        if (mFocusedIndex == codeViewNumber - 1 && s.toString().length() > 0) {
            ((EditText) getChildAt(mFocusedIndex)).setCursorVisible(false);
        }
        if (s.toString().length() < 1) {
            return;
        }
        if (s.toString().length() > 0 && s.toString().length() <= 1) {
            //如果不是最后一个输入框
            if (mFocusedIndex != codeViewNumber - 1) {
                mNexInputEditText = ((EditText) getChildAt(mFocusedIndex + 1));
                mNexInputEditText.requestFocus(); // 焦点移动至下一个EditText
            } else {
                //因为最后一个输入框一直获取着焦点,如果键盘未消失一直改变最后一位的数字,会一直回调输入完成的监听,这里让键盘消失
                hideInputMethod(mContext, mNexInputEditText);
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < codeViewNumber; i++) {
                    EditText editText = (EditText) getChildAt(i);
                    builder.append(editText.getText().toString());
                }
                inputCompleteListener.inputSuccess(builder.toString());
                return;
            }

        }
    }

    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            mFocusedIndex = (int) v.getTag();
            mCurrentFocusedEditText = (EditText) v;
            setmCurrentFocusedEditText(mCurrentFocusedEditText);
        } else {
        }
    }

    /**
     * 给EditText 设置键盘的删除键监听
     *
     * @param v
     * @param keyCode
     * @param event
     * @return
     */
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        block300ms(v);
        if (keyCode == KeyEvent.KEYCODE_DEL) {
            if (((EditText) v).getText().length() > 0 && mFocusedIndex == codeViewNumber - 1) {
                ((EditText) v).setText("");
                ((EditText) v).setCursorVisible(true);
                mNexInputEditText = (EditText) v;
            } else if (((EditText) v).getText().length() == 0) {
                if (mFocusedIndex > 0) {
                    mNexInputEditText = ((EditText) getChildAt(mFocusedIndex - 1));
                    mNexInputEditText.setText("");
                    mNexInputEditText.requestFocus();
                } else {
                    mNexInputEditText = ((EditText) getChildAt(0));
                }
            }
        }
        if (keyCode == KeyEvent.KEYCODE_ENTER) {
            reSetInputState();
            return true;
        }

        return false;
    }

    /**
     * 传递焦点
     */
    public void transmitFocus(int keyCode){
        mCurrentFocusedEditText.clearFocus();
        mNexInputEditText.requestFocus();
    }

    public void block300ms(final View paramView) {
        if (null == paramView) {
            return;
        }
        paramView.setClickable(false);

        paramView.postDelayed(new Runnable() {
            @Override
            public void run() {
                paramView.setClickable(true);
            }
        }, 300);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (MotionEvent.ACTION_DOWN == ev.getAction()) {
            mNexInputEditText.requestFocus();
            showInputMethod(mContext, mNexInputEditText);
            return true;
        }
        return false;
    }

    /**
     * 重置输入框的状态
     */
    public void reSetInputState() {
        for (int i = getChildCount() - 1; i >= 0; i--) {
            EditText editText = (EditText) getChildAt(i);
            editText.setText("");
            if (i == 0) {
                mNexInputEditText = editText;
                editText.requestFocus();
                showInputMethod(mContext, editText);
            }
        }
    }

    /**
     * 输入完成的监听
     */
    public interface InputCompleteListener {
        void inputSuccess(String content);
    }

    /**
     * 显示输入法
     */
    public static void showInputMethod(Context context, View view) {
        try {
            InputMethodManager imeOptions = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
            if (imeOptions.isActive(view)) {
                imeOptions.showSoftInput(view, InputMethodManager.SHOW_FORCED);
            }
        } catch (Throwable e) {
        }
    }

    /**
     * 隐藏输入法
     */
    public static void hideInputMethod(Context context, View view) {
        try {
            InputMethodManager imeOptions = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
            if (imeOptions.isActive(view)) {
                // 这里用
                imeOptions.hideSoftInputFromWindow(view.getWindowToken(), 0);
            }
        } catch (Throwable e) {
        }
    }

}

很简单,控制一些光标的传递逻辑以及与键盘的交互逻辑即可。

猜你喜欢

转载自blog.csdn.net/qq_24505921/article/details/83182673