关于Android自定义View实现验证码输入框

简介: 在做校验验证的时候,经常需要手机接收短信,获取验证码,输入验证码,实现起来其实也不难,今天对输入框简单做了一个封装,通过自定义View来实现。

在做校验验证的时候,经常需要手机接收短信,获取验证码,输入验证码,实现起来其实也不难,今天对输入框简单做了一个封装,通过自定义View来实现。

先看效果图:
image.png

采用组合自定义view来实现,引入布局文件,这种方式实现起来虽然简单,但是不够智能通用,需求变动的话就需要改代码,但是也不难改,代码很简单,下面是完整代码,请自行阅读。

public class VerificationCodeView extends RelativeLayout {
    private Context context;
    private OnCodeFinishListener onCodeFinishListener;
    private TextView tvCode1;
    private TextView tvCode2;
    private TextView tvCode3;
    private TextView tvCode4;
    private TextView tvCode5;
    private TextView tvCode6;
    private View v1;
    private View v2;
    private View v3;
    private View v4;
    private View v5;
    private View v6;
    private EditText etCode;
    private List<String> codes = new ArrayList<>();
    private InputMethodManager imm;
 
 
    public OnCodeFinishListener getOnCodeFinishListener() {
        return onCodeFinishListener;
    }
 
    public void setOnCodeFinishListener(OnCodeFinishListener onCodeFinishListener) {
        this.onCodeFinishListener = onCodeFinishListener;
    }
 
    public VerificationCodeView(Context context) {
        super(context);
        this.context = context;
        loadView();
    }
 
    public VerificationCodeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        loadView();
    }
 
    private void loadView() {
        imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        View view = LayoutInflater.from(context).inflate(R.layout.verification_code, this);
        initView(view);
        initEvent();
    }
 
    ArrayList<View> views = new ArrayList<>();
    ArrayList<TextView> tvList = new ArrayList<>();
 
    private void initView(View view) {
        tvCode1 = view.findViewById(R.id.tv_code1);
        tvCode2 = view.findViewById(R.id.tv_code2);
        tvCode3 = view.findViewById(R.id.tv_code3);
        tvCode4 = view.findViewById(R.id.tv_code4);
        tvCode5 = view.findViewById(R.id.tv_code5);
        tvCode6 = view.findViewById(R.id.tv_code6);
        etCode = view.findViewById(R.id.et_code);
        etCode.requestFocus();
        etCode.setFocusable(true);
        v1 = view.findViewById(R.id.v1);
        v2 = view.findViewById(R.id.v2);
        v3 = view.findViewById(R.id.v3);
        v4 = view.findViewById(R.id.v4);
        v5 = view.findViewById(R.id.v5);
        v6 = view.findViewById(R.id.v6);
        views.add(v1);
        views.add(v2);
        views.add(v3);
        views.add(v4);
        views.add(v5);
        views.add(v6);
        tvList.add(tvCode1);
        tvList.add(tvCode2);
        tvList.add(tvCode3);
        tvList.add(tvCode4);
        tvList.add(tvCode5);
        tvList.add(tvCode6);
    }
 
    private void initEvent() {
        //验证码输入
        etCode.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
 
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
            }
 
            @Override
            public void afterTextChanged(Editable editable) {
                if (editable != null && editable.length() > 0) {
                    etCode.setText("");
                    if (codes.size() < 8) {
                        etCode.setCursorVisible(false);
                        codes.add(editable.toString());
                        showCode();
                    }
                    onCodeFinishListener.onTextChange(getPhoneCode());
                }
            }
        });
        // 监听验证码删除按键
        etCode.setOnKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
                if (keyCode == KeyEvent.KEYCODE_DEL && keyEvent.getAction() == KeyEvent.ACTION_DOWN && codes.size() > 0) {
                    codes.remove(codes.size() - 1);
                    showCode();
                    onCodeFinishListener.onTextDel();
                    return true;
                }
                return false;
            }
        });
    }
 
    /**
     * 显示输入的验证码
     */
    private void showCode() {
        String code1 = "";
        String code2 = "";
        String code3 = "";
        String code4 = "";
        String code5 = "";
        String code6 = "";
        if (codes.size() >= 1) {
            code1 = codes.get(0);
        }
        if (codes.size() >= 2) {
            code2 = codes.get(1);
        }
        if (codes.size() >= 3) {
            code3 = codes.get(2);
        }
        if (codes.size() >= 4) {
            code4 = codes.get(3);
        }
        if (codes.size() >= 5) {
            code5 = codes.get(4);
        }
        if (codes.size() >= 6) {
            code6 = codes.get(5);
        }
 
        tvCode1.setText(code1);
        tvCode2.setText(code2);
        tvCode3.setText(code3);
        tvCode4.setText(code4);
        tvCode5.setText(code5);
        tvCode6.setText(code6);
    }
 
    /**
     * 设置下划线高亮颜色
     */
    private void setColor(int defaultColor, int focusColor) {
        int color_default = context.getResources().getColor(defaultColor);
        int color_focus = context.getResources().getColor(focusColor);
        for (View v : views) {
            //默认颜色
            v.setBackgroundColor(color_default);
        }
        int length = getPhoneCode().length();
        for (int i = 0; i < length; i++) {
            views.get(i).setBackgroundColor(color_focus);
        }
    }
 
    /**
     * 显示键盘
     */
    public void showSoftInput() {
        //显示软键盘
        if (imm != null && etCode != null) {
            etCode.postDelayed(new Runnable() {
                @Override
                public void run() {
                    imm.showSoftInput(etCode, 0);
                }
            }, 200);
        }
    }
 
    /**
     * 获得手机号验证码
     *
     * @return 验证码
     */
    public String getPhoneCode() {
        StringBuilder sb = new StringBuilder();
        for (String code : codes) {
            sb.append(code);
        }
        return sb.toString();
    }
 
    public void errorText(int color) {
        for (View v : views) {
            v.setBackgroundColor(color);
        }
    }
 
    public void textBgColor(int color) {
        for (View v : views) {
            v.setBackgroundColor(color);
        }
    }
 
    public interface OnCodeFinishListener {
        /**
         * 文本改变
         */
        void onTextChange(String content);
 
        void onTextDel();
    }
 
 
    public void setEditTextCursorDrawable(EditText editText) {
        //修改光标的颜色(反射)
        try {
            @SuppressLint("SoonBlockedPrivateApi") Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
            f.setAccessible(true);
//            f.set(editText, R.drawable.et_otp_cursor);
        } catch (Exception ignored) {
        }
    }
}

在看看布局文件

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    <LinearLayout
        android:id="@+id/ll_code"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:layout_marginRight="10dp">
            <TextView
                android:id="@+id/tv_code1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/black"
                android:textSize="24dp"
                android:textStyle="bold"
                android:background="@null"
                android:gravity="center"/>
            <View
                android:id="@+id/v1"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="#4c000000" />
        </LinearLayout>
 
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:layout_marginRight="10dp"
            >
            <TextView
                android:id="@+id/tv_code2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/black"
                android:textSize="24dp"
                android:textStyle="bold"
                android:background="@null"
                android:gravity="center"/>
            <View
                android:id="@+id/v2"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="#4c000000" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:layout_marginRight="10dp"
            >
            <TextView
                android:id="@+id/tv_code3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/black"
                android:textSize="24dp"
                android:textStyle="bold"
                android:background="@null"
                android:gravity="center"/>
            <View
                android:id="@+id/v3"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="#4c000000" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:layout_marginRight="10dp">
            <TextView
                android:id="@+id/tv_code4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/black"
                android:textSize="24dp"
                android:textStyle="bold"
                android:background="@null"
                android:gravity="center"/>
            <View
                android:id="@+id/v4"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="#4c000000" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:layout_marginRight="10dp">
            <TextView
                android:id="@+id/tv_code5"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@null"
                android:textColor="@color/black"
                android:textSize="24dp"
                android:textStyle="bold"
                android:gravity="center"/>
            <View
                android:id="@+id/v5"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="#4c000000" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:layout_marginRight="10dp"
            >
            <TextView
                android:id="@+id/tv_code6"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textColor="@color/black"
                android:textSize="24dp"
                android:textStyle="bold"
                android:background="@null"
                android:gravity="center"/>
            <View
                android:id="@+id/v6"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="#4c000000" />
        </LinearLayout>
    </LinearLayout>
 
    <EditText
        android:id="@+id/et_code"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/ll_code"
        android:layout_alignBottom="@+id/ll_code"
        android:background="@android:color/transparent"
        android:textColor="@android:color/transparent"
        android:cursorVisible="false"
        android:layout_marginLeft="20dp"
        android:inputType="number"/>
</RelativeLayout>
相关文章
|
6月前
|
Android开发 UED 计算机视觉
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
517 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
|
6月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
322 65
Android自定义view之网易云推荐歌单界面
|
6月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
664 84
|
6月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
185 3
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
143 2
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
351 3
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
322 3
|
消息中间件 前端开发 Android开发
Android面试题自定义View之Window、ViewRootImpl和View的三大流程
Android开发中,View的三大核心流程包括measure(测量)、layout(布局)和draw(绘制)。MeasureSpec类在测量过程中起到关键作用,它结合尺寸大小和模式(EXACTLY、AT_MOST、UNSPECIFIED)来指定View应如何测量。onMeasure方法用于自定义View的测量,布局阶段,ViewGroup调用onLayout确定子元素位置,而draw阶段按照特定顺序绘制背景、内容、子元素和装饰。整个流程始于ViewRootImpl的performTraversals,该方法触发测量、布局和绘制。
307 0