自定义view之实现验证码输入框

404 阅读2分钟

前言

最近写外包遇到一个需求要我写一个类似于滴滴什么的那种验证码输入框,到网上查了一下,有现成的,但基于自己自定义view薄弱这一块,决定自己实现一下

如何实现

网络上大部分的实现办法都是在一个Edittext上加上一个textview数组,由textview来实现这个效果,而我也恰巧是这么想的,这里可以画个图来实例一下 

 大体的设计思路就体现在了这个图里面,下面我们开始实现一下

布局

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:id="@+id/contanier"
        android:layout_centerInParent="true"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:showDividers="middle"></LinearLayout>

    <EditText
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/edit"
        android:inputType="number"/>
</RelativeLayout>

这是一个自定义viewgroup

核心代码

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int mheightMeasure=heightMeasureSpec;
        int heightMode=MeasureSpec.getMode(heightMeasureSpec);
        if (heightMode==MeasureSpec.AT_MOST){
            mheightMeasure=MeasureSpec.makeMeasureSpec((int)dp2px(50,getContext()),MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, mheightMeasure);
    }

首先由于是自定义viewgroup,wrapcontent失效(自定义view中wrapcontent等价于matchparent),所以我们要自定义下warpcontent。之后开始初始化我们的部件

    private LinearLayout mContainerLi;
    private EditText mInputEt;
    private TextView[] textViews;
    private int mEtnumber;
    private int mTvCountnumber;
    private int mEtWidth;
    private int mAlpha;
    private Drawable mEtDividerDrawable;
    private int mEtTextColor;
    private float mEtTextSize;
    private Drawable mBackGroundDrawable;

    private ActivationCodeTextWatcher mTextWatcher=new ActivationCodeTextWatcher();

这其中包括textview即那个正方形的宽高透明度,字体大小,颜色等还有控件背景图片,以及一个我们自定义实现的textview的观测工具类

private class ActivationCodeTextWatcher implements TextWatcher {

        @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 editable) {
            String inputStr = editable.toString();
            if (inputStr != null && !inputStr.equals("")) {
                setText(inputStr);
                mInputEt.setText("");
            }
        }
    }

接着开始初始化控件

 private void initUI() {
        initTextViews(getContext(),mEtnumber,mEtWidth,mEtDividerDrawable,mEtTextSize,mEtTextColor);
        initcontainer(textViews);
        setListener();

    }

小方块的初始化

  private void initTextViews(Context context, int mEtnumber, int mEtWidth, Drawable mEtDividerDrawable, float mEtTextSize, int mEtTextColor) {
        mInputEt.setCursorVisible(false);
        mInputEt.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mEtnumber)});
        if (mEtDividerDrawable!=null){
            mEtDividerDrawable.setBounds(0,0,mEtDividerDrawable.getMinimumWidth(),mEtDividerDrawable.getMinimumHeight());
            mContainerLi.setDividerDrawable(mEtDividerDrawable);
        }
        textViews=new TextView[mEtnumber];
        for (int i=0;i<textViews.length;i++){
            TextView textView=new TextView(context);
            textView.setTextSize(mEtTextSize);
            textView.setTextColor(mEtTextColor);
            textView.setWidth(mEtWidth);
            textView.setHeight(mEtWidth);
            textView.setBackground(mBackGroundDrawable);

            textView.setAlpha(mAlpha);
            textView.setGravity(Gravity.CENTER);
            textView.setFocusable(false);
            textViews[i]=textView;
        }
    }

首先要初始化小方块,在linearlayout中,先将光标隐藏并设置可输入最大值防止bug,接着设置linearlayout的divider,接着为textview集合中的textview设置属性

将这些小方块装入linearlayout中

private void initcontainer(TextView[] textViews) {
        for (int i=0;i<textViews.length;i++){
            mContainerLi.addView(textViews[i]);
        }
    }

自定义实现输入,删除

private void setListener() {
        mInputEt.addTextChangedListener(mTextWatcher);

        mInputEt.setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (keyCode==KeyEvent.KEYCODE_DEL&&event.getAction()==KeyEvent.ACTION_DOWN){
                    onKeyDelete();
                    return true;
                }
                return false;
            }

        });
    }

private void onKeyDelete() {
        for (int i = textViews.length - 1; i >= 0; i--) {
            TextView tv = textViews[i];
            if (!tv.getText().toString().trim().equals("")) {

                tv.setText(tv.getText().subSequence(0,tv.getText().length()-1));
                // 添加删除完成监听
                if (inputCompleteListener != null) {
                    inputCompleteListener.deleteContent();
                }
                tv.setBackgroundDrawable(mBackGroundDrawable);
                if (i < mEtnumber - 1) {
                    textViews[i + 1].setBackgroundDrawable(mBackGroundDrawable);
                }
                break;
            }
        }
    }
    public void setText(String inputContent){
        for (int i=0;i<textViews.length;i++){
            TextView tv=textViews[i];
            if (tv.getText().toString().trim().equals("")||tv.getText().toString().length()<mTvCountnumber){
                tv.setText(tv.getText().toString()+inputContent);
                if (inputCompleteListener!=null){
                    inputCompleteListener.inputComplete();
                }
                if (tv.getText().toString().length()==mTvCountnumber){
                    tv.setBackgroundDrawable(mBackGroundDrawable);
                    if (i<mEtnumber-1){
                        textViews[i+1].setBackgroundDrawable(mBackGroundDrawable);
                    }
                }
                break;
            }
        }
    }

其中这两个函数是关键,一个是删除,一个是添加。

实现效果

最后让我们看一下实现效果

宣告完成