Android自定义短信验证码组件

Android自定义短信验证码组件

效果图

  • 1.布局实现

1.因为要禁用光标,所以我用TextView代替了EditText,每一行显示的验证码个数由用户决定,所以我这里用线性布局的权重,对TextView进行控制宽度等分,然后设置选中和未选中当前TextView的底部边框,设置高亮颜色背景

  • 2.接受用户输入

我这里使用了TextView,但是怎么接受用户输入的值呢。这里我直接继承了RelativeLayout,然后添加了一个透明的EditText,覆盖在这几个TextView上面,用户就可以点击唤起键盘输入

  • 3.TextView如何显示值和删除值

我这里设置EditText的TextColor和BackgroundColor为Color.TRANSPARENT 透明,然后监听EditText的addTextChangedListener事件和setOnKeyListener按键删除事件,然后把值添加到TextView上,就能实现了,然后在写一个对外的接口。获取到输入的验证码。

  • 4.添加闪烁的动画

我这里使用了ObjectAnimator,初始化给每个TextView添加动画,然后在输入的时候给当前的TextView启动动画,取消其他TextView动画

具体源码如下:


/**
 * @author wu_ming_zhi_bei
 * @date 2021/1/27 15:00
 * @Notes
 */
public class VerificationCodeView extends RelativeLayout {
    private Context mContext;
    private RelativeLayout.LayoutParams layoutParams;
    private int num = 4;//验证码数量
    private int codeSize;//字体大小
    private int codeColor;//字体颜色
    private List<TextView> tvs = new ArrayList<>();
    private List<String> codes = new ArrayList<>();
    private EditText etCode;
    private InputMethodManager imm;
    List<ObjectAnimator> animators = new ArrayList<>();

    public VerificationCodeView(Context context) {
        this(context,null);
    }

    public VerificationCodeView(Context context, AttributeSet attrs) {
        this(context,attrs,0);
    }

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

    private void init(Context context, AttributeSet attrs) {
        this.mContext = context;
        imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);

        TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.VerificationCodeView);
        num = a.getInteger(R.styleable.VerificationCodeView_num,4);
        codeSize = a.getDimensionPixelSize(R.styleable.VerificationCodeView_codeSize,18);
        codeColor = a.getColor(R.styleable.VerificationCodeView_codeColor,getResources().getColor(R.color.theme_color));
        //初始化一个大的LinearLayout来存放验证码
        LinearLayout codeBox = new LinearLayout(mContext);
        codeBox.setOrientation(LinearLayout.HORIZONTAL);
        codeBox.setGravity(Gravity.CENTER);
        //添加方块
        for(int i=0;i<num;i++){
            TextView tv = new TextView(mContext);
            tv.setTextSize(codeSize);
            tv.setTextColor(codeColor);
            tv.setGravity(Gravity.CENTER);
            tv.setPadding(0,0,0,10);
            LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(
                    LayoutParams.MATCH_PARENT,
                    LayoutParams.MATCH_PARENT, 1);
            param.gravity = Gravity.CENTER;
            param.rightMargin = 20;
            param.leftMargin = 20;
            param.topMargin = 20;
            param.bottomMargin = 20;
            tv.setLayoutParams(param);
            //默认第一个选中
            if(i==0){
                tv.setText("|");
                tv.setBackground(mContext.getResources().getDrawable(R.drawable.code_border_current_bottom));
            }else{
                tv.setBackground(mContext.getResources().getDrawable(R.drawable.code_border_bottom));
            }
            codeBox.addView(tv);
            tvs.add(tv);//添加到数组
        }
        layoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        //添加codebox的位置在组件的最上面
        layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP,TRUE);
        layoutParams.setMargins(60,0,60,0);
        codeBox.setLayoutParams(layoutParams);

        //添加Edit
        etCode = new EditText(mContext);
        etCode.setLayoutParams(layoutParams);
        etCode.setLines(1);
        etCode.setMaxLines(1);
        etCode.setTextColor(Color.TRANSPARENT);
        etCode.setBackgroundColor(Color.TRANSPARENT);
        etCode.setCursorVisible(false);
        //添加edit监听
        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() < num){
                        codes.add(editable.toString());
                        showCode();
                    }
                }
            }
        });

        // 监听验证码删除按键
        etCode.setOnKeyListener(new View.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();
                    return true;
                }
                return false;
            }
        });
        addView(codeBox);
        addView(etCode);
        addAnimation();//添加东安湖
        setTwinkle();//开启动画
    }

    //显示验证码
    private void showCode(){
        int size = codes.size();//1 6
        for(int i=0;i<num;i++){
            if(size>i){
                tvs.get(i).setText(codes.get(i));//添加到textview
            }else if(size==i){
                tvs.get(i).setText("|");
            }else{
                tvs.get(i).setText("");
            }
        }
        etCode.setFocusable(true);
        etCode.requestFocus();
        etCode.setFocusableInTouchMode(true);
        etCode.requestFocusFromTouch();
        setTwinkle();//动画
        callBack();//回调

    }

    private void showColor(){
        int size = codes.size();
        if(size==0){
            tvs.get(0).setBackground(mContext.getResources().getDrawable(R.drawable.code_border_current_bottom));
        }else{
            for(int i=0;i<tvs.size();i++){
                if(i==size-1){
                    tvs.get(i).setBackground(mContext.getResources().getDrawable(R.drawable.code_border_current_bottom));
                }else{
                    tvs.get(i).setBackground(mContext.getResources().getDrawable(R.drawable.code_border_bottom));
                }
            }
        }
    }

    /**
     * 回调
     */
    private void callBack(){
        if(onInputListener==null){
            return;
        }
        if(codes.size()==num){
            onInputListener.onSucess(getPhoneCode());
        }else{
            onInputListener.onInput();
        }
    }

    //定义回调
    public interface OnInputListener{
        void onSucess(String code);
        void onInput();
    }
    private OnInputListener onInputListener;
    public void setOnInputListener(OnInputListener onInputListener){
        this.onInputListener = onInputListener;
    }

    /**
     * 获得手机号验证码
     * @return 验证码
     */
    public String getPhoneCode(){
        StringBuilder sb = new StringBuilder();
        for (String code : codes) {
            sb.append(code);
        }
        return sb.toString();
    }


    /**
     * 显示键盘
     */
    public void showSoftInput(){
        //显示软键盘
        if(imm!=null && etCode!=null) {
            etCode.postDelayed(new Runnable() {
                @Override
                public void run() {
                    imm.showSoftInput(etCode, 0);
                }
            },200);
        }
    }

    /**
     * 隐藏键盘
     */
    public void hideSoftInput(){
        //显示软键盘
        if(imm!=null && etCode!=null) {
            etCode.postDelayed(new Runnable() {
                @Override
                public void run() {
                    imm.hideSoftInputFromWindow(etCode.getWindowToken(), 0);
                }
            },200);
        }
    }

    /**
     * 添加动画
     */
    private void addAnimation(){
        for(int i=0;i<tvs.size();i++){
            ObjectAnimator animator = ObjectAnimator.ofInt(tvs.get(i), "TextColor", 0x00000000, 0xfff00000);
            animator.setDuration(10000);
            final int index = i;
            animator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {

                }

                @Override
                public void onAnimationEnd(Animator animator) {

                }

                @Override
                public void onAnimationCancel(Animator animator) {
                    tvs.get(index).setTextColor(codeColor);
                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
            animator.setInterpolator(new LinearInterpolator());
            animator.setRepeatCount(Animation.INFINITE);
            animators.add(animator);
        }
    }

    /**
     * 开启动画
     */
    private void setTwinkle(){
        int size = codes.size();
        for(int i=0;i<tvs.size();i++){
            if(i==size){
                animators.get(i).start();
                tvs.get(i).setBackground(mContext.getResources().getDrawable(R.drawable.code_border_current_bottom));
            }else{
                animators.get(i).cancel();
                tvs.get(i).setBackground(mContext.getResources().getDrawable(R.drawable.code_border_bottom));
            }
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        for(int i=0;i<tvs.size();i++){
            animators.get(i).cancel();
        }
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Unknown world

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值