Androoid 验证码输入框控件

项目中用到6位验证码布局,看了网上的开源组件,很简单,只是自己单纯自己想去写一个,所以提供给大家,效果如下:


实现方式很多,笔者重写了GridLayout,动态添加Edittext,话不多说先看下源码:

public class EdtSmsCodeLayout extends GridLayout implements TextWatcher, View.OnKeyListener {

    private EditText[] edts;

    private int edt_position = 0;

    private StringBuffer code;

    private InputFinishListener inputFinishListener;

    private TypedArray typedArray;

    private int maxLen;    // 布局总长度 默认六位
    private int txtSize;  // 文本大小
    private int txtColor;    // 文本颜色
    private int edtSize;    // 每个输入框长宽

    public EdtSmsCodeLayout(Context context) {
        super(context);
        init(null);
    }

    public EdtSmsCodeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EdtSmsCodeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    public EditText[] getEdts() {
        return edts;
    }

    private void init(AttributeSet set) {
        initDefValue();
        initSet(set);
        code = new StringBuffer();
        edts = initEdts(maxLen);
        edts[edt_position].setFocusableInTouchMode(true);
        edts[edt_position].requestFocus();
        final InputMethodManager imm = (InputMethodManager) getContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            public void run() {
                imm.showSoftInput(edts[edt_position], 0);
            }
        }, 500);
    }

    /**
     * 初始化默认值
     */
    private void initDefValue() {
        maxLen = DefValue.MAX_LEN.value;
        txtSize = DefValue.TXT_SIZE.value;
        txtColor = getContext().getResources().getColor(DefValue.TXT_COLOR.value);
        edtSize = dip2px(DefValue.SIZE.value);
    }

    /**
     * 初始化输入框
     *
     * @param maxLen 输入框个数 默认6
     * @return
     */
    private EditText[] initEdts(int maxLen) {
        EditText[] editTexts = new EditText[maxLen];
        for (int i = 1; i <= maxLen; i++) {
            final EditText editText = new EditText(getContext());
            editText.setInputType(InputType.TYPE_CLASS_NUMBER);
            editText.setOnClickListener(null);
            editText.setLayoutParams(new LayoutParams(new LinearLayout.LayoutParams((edtSize), (edtSize))));
            editText.setGravity(Gravity.CENTER);
            editText.setTextSize(txtSize);
            editText.setTextColor(txtColor);
            editText.addTextChangedListener(this);
            editText.setOnKeyListener(this);
            Field f = null;
            try {
                f = TextView.class.getDeclaredField("mCursorDrawableRes");
                f.setAccessible(true);
                f.set(editText, R.drawable.edt_cursor);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            setEdtBg(editText, i, maxLen);
            editText.setFilters(new InputFilter[]{
                    new InputFilter.LengthFilter(1)});
            editTexts[i - 1] = editText;
            addView(editText);
            editText.setFocusableInTouchMode(false);
        }
        return editTexts;
    }

    /**
     * 初始化xml set
     *
     * @param set
     */
    private void initSet(AttributeSet set) {
        if (set == null)
            return;
        typedArray = getContext().obtainStyledAttributes(set, R.styleable.EdtSmsCodeLayout);
        if (typedArray != null) {
            txtSize = typedArray.getDimensionPixelSize(R.styleable.EdtSmsCodeLayout_text_size, -1) == -1 ? txtSize : px2dip(typedArray.getDimensionPixelSize(R.styleable.EdtSmsCodeLayout_text_size, -1));
            txtColor = typedArray.getColor(R.styleable.EdtSmsCodeLayout_text_color, txtColor);
            edtSize = typedArray.getDimensionPixelSize(R.styleable.EdtSmsCodeLayout_item_size, edtSize);
            maxLen = typedArray.getInt(R.styleable.EdtSmsCodeLayout_max_len, maxLen);
        }
    }

    public void setInputFinishListener(InputFinishListener listener) {
        inputFinishListener = listener;
    }

    private void setEdtBg(EditText text, int position, int maxLen) {
        if (position == 1)
            text.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_edt_sms_left));
        else if (position == 2)
            text.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_edt_sms_second));
        else if (position == maxLen)
            text.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_edt_sms_right));
        else
            text.setBackgroundDrawable(getResources().getDrawable(R.drawable.bg_edt_sms));

    }

    @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 (edts[edt_position].getText().toString().length() >= 1) {
            if (edt_position == edts.length - 1) {
                code.append(edts[edt_position].getText().toString());
                if (inputFinishListener != null)
                    inputFinishListener.onInputFinish(code.toString());
                removeCode();
                return;
            }
            nextEdt();
        }
    }

    @Override
    public void afterTextChanged(Editable s) {

    }

    private int dip2px(float dpValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    private int px2dip(float pxValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    private void nextEdt() {
        setEdtFocus(1);
    }

    private void backEdt() {
        if (edt_position == 0)
            return;
        edts[edt_position - 1].setText(null);
        setEdtFocus(-1);
    }

    /**
     * 清空验证码,默认输入完成后清空,也可手动调用
     */
    public void removeCode() {
        edt_position = 0;
        code.delete(0, code.length());
        for (EditText text : edts) {
            text.setFocusableInTouchMode(false);
            text.setText(null);
        }
        edts[edt_position].setFocusableInTouchMode(true);
        edts[edt_position].requestFocus();
    }

    private void setEdtFocus(int type) {
        if (type > 0) {
            code.append(edts[edt_position].getText().toString());
        } else {
            code.deleteCharAt(code.length() - 1);
        }
        edts[edt_position].setFocusableInTouchMode(false);
        edt_position += type;
        edts[edt_position].setFocusableInTouchMode(true);
        edts[edt_position].requestFocus();
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN)
            backEdt();
        return false;
    }

    public interface InputFinishListener {
        void onInputFinish(String code);
    }
}
enum DefValue {
    TXT_SIZE(16),
    TXT_COLOR(R.color.light_black),
    SIZE(40),
    MAX_LEN(6);

    int value;

    DefValue(int value) {
        this.value = value;
    }
}
其实重写GridLayout跟LinearLayou效果是一样的,都是通过addview()方式把childview添加进去,具体实现就不讲了,类中方法很清晰,主要是item间的分割线,我想到的办法是动态的为每个item去设置背景,具体方法是setEdtBg(),下面是四种分割线布局,把他们拼到布局中:

bg_edt_sms_left.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <corners android:bottomLeftRadius="8dp" android:bottomRightRadius="0dp" android:topLeftRadius="8dp" android:topRightRadius="0dp" />
            <solid android:color="@android:color/white" />
            <stroke android:width="0.5dp" android:color="@android:color/darker_gray" />
        </shape>
    </item>
</selector>
bg_edt_sms_right.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <corners android:bottomLeftRadius="8dp" android:bottomRightRadius="0dp" android:topLeftRadius="8dp" android:topRightRadius="0dp" />
            <solid android:color="@android:color/white" />
            <stroke android:width="0.5dp" android:color="@android:color/darker_gray" />
        </shape>
    </item>
</selector>
bg_edt_sms_second.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="@android:color/darker_gray" />
        </shape>
    </item>
    <item
        android:bottom="0.8dp"
        android:top="0.8dp">
        <shape>
            <solid android:color="@android:color/white" />
        </shape>
    </item>

</layer-list>

bg_edt_sms.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="@android:color/darker_gray" />
        </shape>
    </item>
    <item
        android:bottom="0.8dp"
        android:top="0.8dp"
        android:left="0.8dp">
        <shape>
            <solid android:color="@android:color/white" />
        </shape>
    </item>

</layer-list>

使用:

在gradle(project)配置中添加:
allprojects {
    repositories {
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}
在gradle(app)配置中添加:
compile 'com.github.Yuhoon:EdtSmsCode:v0.12'

在xml中添加组件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.wcong.edtsmscode.EdtSmsCodeLayout
        android:id="@+id/edt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        app:item_size="40dp"
        app:max_len="6"
        app:text_color="@color/colorPrimary"
        app:text_size="16dp" />
</RelativeLayout>

activity中引用:

((EdtSmsCodeLayout) findViewById(R.id.edt)).setInputFinishListener(new EdtSmsCodeLayout.InputFinishListener() {
    @Override
    public void onInputFinish(String code) {
        Toast.makeText(MainActivity.this, code, Toast.LENGTH_SHORT).show();
    }
});
在inputFinish接口中拿到输入框中验证码。

定义了4个可设置的控件属性:

app:item_size="40dp" 每个Edittext的长宽,默认值40

app:max_len="6" Edittext个数,默认为6

app:text_size="16dp" 文字大小,默认为16

app:text_color="@color/colorPrimary" 文字颜色,默认为#333333 黑

项目git地址: 项目地址
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值