android 中 自己实现验证码表格填写的效果

项目中,遇到了这样要求:实现6位验证码填写的效果,验证码是数字,输入一个后自动跳转到下一个,然后可以删除回退,界面效果如下所示:

image.png

特别说明

没有使用第三方开源库,这可能是比较烦琐的一种原始的实现方法,也没有特意去封装使用,主要理解一下实现思路即可,实现方式应该有很多种,思路其实很重要的。

实现原理

其实是比较简单的,每一个验证码号都是一个EditText,当一个输入完成,通过判断字段的长度,看看可输入的长度是不是为1,是的话然后让下一个EditText获取焦点,以此类推,然后对EditText进行各种监听管理:例如删除、删除回退、是否是最后一个等等

核心代码实现

将验证码的6个EditText添加到集合中管理,默认第一个获取焦点


image.png

image.png

image.png

对EditText进行监听处理


image.png

image.png

image.png

image.png

image.png

完整代码如下

1、界面布局




    

    

        

        

        

            

            

            

            

            

            

            

            

            

            

            

            

            

        


        

            

            

            

            

            

            

            

            

            

            

            

            

            

        

    


    

2、界面实现逻辑

/**
 * 验证码页面
 */
public class VerificationCodeActivity extends BaseActivity {

    private static final String TAG = VerificationCodeActivity.class.getSimpleName();
    @BindView(R.id.common_titleBar_iv_back)
    ImageView commonTitleBarIvBack;
    @BindView(R.id.common_titleBar_tv_title)
    TextView commonTitleBarTvTitle;
    @BindView(R.id.tv_phone_num)
    TextView tvPhoneNum;
    @BindView(R.id.textView2)
    TextView textView2;
    @BindView(R.id.et_num_one)
    EditText etNumOne;
    @BindView(R.id.et_num_two)
    EditText etNumTwo;
    @BindView(R.id.et_num_three)
    EditText etNumThree;
    @BindView(R.id.et_num_four)
    EditText etNumFour;
    @BindView(R.id.bt_start)
    Button btStart;

    @BindView(R.id.rootview)
    View mRootView;
    @BindView(R.id.et_num_five)
    EditText etNumFive;
    @BindView(R.id.et_num_six)
    EditText etNumSix;

    private int mFlag;
    private boolean mIsRegist;
    private String mPhoneNum;

    private ArrayList etLists = new ArrayList<>();

    private SystemInfoBean mSystemInfoBean;

    private LatitudeAndLongitudeBean mLatitudeAndLongitudeBean;

    private String mLoginLatitude;
    private String mLoginLongitude;

    @Override
    public int setLayout() {
        return R.layout.activity_verification_code;
    }

    @Override
    public void init() {
        initData();
        initListener();
        initOperation();
    }

    /**
     * 初始化数据的方法
     */
    private void initData() {
        commonTitleBarTvTitle.setText("填写验证码");
        Intent intent = getIntent();
        if (intent != null) {
            mFlag = intent.getIntExtra(Constants.ACTIVITY_PAGE_FLAG, 0);
            mIsRegist = intent.getBooleanExtra(Constants.ISREGIST, false);
            mPhoneNum = intent.getStringExtra(Constants.PHONE_NUM);

            mSystemInfoBean = (SystemInfoBean)intent.getSerializableExtra(SYSTEM_INFO);
            if (mSystemInfoBean != null){
                mLatitudeAndLongitudeBean = mSystemInfoBean
                        .mLatitudeAndLongitudeBean;
                if (mLatitudeAndLongitudeBean != null){
                    mLoginLatitude = mLatitudeAndLongitudeBean.loginLatitude;
                    mLoginLongitude = mLatitudeAndLongitudeBean.loginLongitude;
                }
            }

            if (!TextUtils.isEmpty(mPhoneNum)) {
                tvPhoneNum.setText("+86  " + mPhoneNum);
            }
        }

        etLists.clear();

        etLists.add(etNumOne);
        etLists.add(etNumTwo);
        etLists.add(etNumThree);
        etLists.add(etNumFour);
        etLists.add(etNumFive);
        etLists.add(etNumSix);

        showInputManager(etNumOne);
    }

    private void showInputManager(EditText editText) {
        editText.setFocusable(true);
        editText.setFocusableInTouchMode(true);
        editText.requestFocus();

        /** 目前测试来看,还是挺准的
         * 原理:OnGlobalLayoutListener 每次布局变化时都会调用
         * 界面view 显示消失都会调用,软键盘显示与消失时都调用
         * */
        mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mLayoutChangeListener);
        InputMethodManager inputManager =
                (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        inputManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);

    }

    public void showSoftInput(EditText input) {
        input.setFocusable(true);
        input.requestFocus();
        InputMethodManager inputMethodManager = (InputMethodManager) mContext.getSystemService
                (Context.INPUT_METHOD_SERVICE);
        inputMethodManager.showSoftInput(input, InputMethodManager.SHOW_IMPLICIT);
    }


    /**
     * 初始化监听的方法
     */
    private void initListener() {
        editTextListener(etNumOne, etNumTwo);
        editTextListener(etNumTwo, etNumThree);
        editTextListener(etNumThree, etNumFour);
        editTextListener(etNumFour, etNumFive);
        editTextListener(etNumFive, etNumSix);
    }

    /**
     * 初始化操作的方法
     */
     private void initOperation() {
         if (!TextUtils.isEmpty(mPhoneNum)){
             send(mPhoneNum, "1");
         }
     }

    @OnClick({R.id.common_titleBar_iv_back, R.id.bt_start})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.common_titleBar_iv_back:
                finish();
                break;
            case R.id.bt_start:

                String codeValue = getCodeValue(etLists);
                if (!TextUtils.isEmpty(codeValue)){
                    boolean isCodeValue = isCodeValue(codeValue);
                    if (isCodeValue){
                        codePast(mPhoneNum);
                    }
                }else {
                    ToastUtil.toast(mContext,"验证码不能为空");
                }
                break;
        }
    }

    /**
     * 通过flag来区分是跳转到修改密码还是完善资料
     *
     * @param flag
     */
    private void checkAction(int flag) {
        switch (flag) {
            case Constants.FORGET_TO_VERIFICATIONCODE:
                goToResetPassword();
                break;
            case Constants.REGIST_TO_PERFECT:
                goToPerfectData();
                break;
        }
    }

    /**
     * 前往信息完善页面
     */
    private void goToPerfectData() {
        Intent intent = new Intent(mContext,PerfectDataActivity.class);
        intent.putExtra(SYSTEM_INFO,mSystemInfoBean);
        startActivity(intent);
        finish();
    }

    /**
     * 从忘记密码的流程过来,前往重设密码的界面
     */
    private void goToResetPassword() {
        Intent intent = new Intent(mContext,ChangePwdActivity.class);
        intent.putExtra(SYSTEM_INFO,mSystemInfoBean);
        startActivity(intent);
        finish();
    }

    /**
     * 4个EditText自动切换
     */
    private void autoMoveEdit(EditText firstEditText, EditText twoEditText) {
        if (firstEditText.isFocused()) {
            LogUtils.d(TAG, "autoMoveEdit======");
            String oneCode = firstEditText.getText().toString();
            firstEditText.setSelection(oneCode.length());
            if (oneCode.length() == 1) {
                setFocu(firstEditText, twoEditText);
            }
        }
    }


    /**
     * 设置焦点
     */
    private void setFocu(EditText beforeEt, EditText pEt) {
        pEt.setFocusable(true);
        pEt.setFocusableInTouchMode(true);
        pEt.requestFocus();
        pEt.setSelection(pEt.getText().toString().trim().length());
        delFocu(beforeEt, pEt);
    }

    /**
     * 监听软键盘的删除键,删除
     *
     * @param beforeEt
     * @param nextEt
     */
    private void delFocu(final EditText beforeEt, final EditText nextEt) {
        nextEt.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (nextEt.equals(etLists.get(0))) {
                    return true;
                }
                String s = beforeEt.getText().toString();
                LogUtils.d(TAG, "delFocu============s============" + s);
                if (!TextUtils.isEmpty(s)) {
                    beforeEt.setText("");
                    beforeEt.setFocusable(true);
                    beforeEt.setFocusableInTouchMode(true);
                    beforeEt.requestFocus();
                }
                return false;
            }
        });
    }


    /**
     * 对EditText监听
     *
     * @param beforeEdit
     * @param afterEdit
     */
    private void editTextListener(final EditText beforeEdit, final EditText afterEdit) {
        beforeEdit.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                LogUtils.d(TAG, "beforeTextChanged======");
            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                LogUtils.d(TAG, "onTextChanged======");
                autoMoveEdit(beforeEdit, afterEdit);
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
    }

    /**
     * 获取手动输入的验证码的值
     */
    private String getCodeValue(ArrayList list){
        StringBuilder sb = new StringBuilder();
        if (list != null){
            int size = list.size();
            if (size > 0){
                for (int i = 0; i < size; i++) {
                    EditText editText = list.get(i);
                    String s = editText.getText().toString();
                    if (!TextUtils.isEmpty(s)){
                        sb.append(s);
                    }

                }
            }
        }
        return sb.toString();
    }


    /**
     * 判断有没有输入6位验证码
     */
    private boolean isCodeValue(String codeValue){
        int length = codeValue.length();
        if (length == 6){

            return  true;
        }else {
            return  false;
        }

    }

    ViewTreeObserver.OnGlobalLayoutListener mLayoutChangeListener = new ViewTreeObserver
            .OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //判断窗口可见区域大小
            Rect r = new Rect();
            // getWindowVisibleDisplayFrame()会返回窗口的可见区域高度
            getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
            //如果屏幕高度和Window可见区域高度差值大于整个屏幕高度的1/3,则表示软键盘显示中,否则软键盘为隐藏状态。
            int heightDifference = WindowManagerUtils.getScreenHight() - (r.bottom);
            boolean isKeyboardShowing = heightDifference > WindowManagerUtils.getScreenHight() / 3;
            if (isKeyboardShowing) {
//                D.i("slack","show..."+ r.bottom + " - " + r.top + " = " + (r.bottom - r.top)
// +","+ heightDifference);
                // bottomView 需要跟随软键盘移动的布局
                // setDuration(0) 默认300, 设置 0 ,表示动画执行时间为0,没有过程,只有动画结果了
                btStart.animate().translationY(-heightDifference).setDuration(0).start();
            } else {
//                D.i("slack","hide...");
                btStart.animate().translationY(0).start();
            }
        }
    };

    /**
     * 短信发送接口
     */
    private void send(String phone, String type){
        SendParam param = new SendParam(phone,type);
        AppRequestImpl.getInstance( ).send(phone,type,new ProgressSubscriber>(mContext,getString(R.string.tip_is_send_code), new SubscriberResultListener() {
            @Override
            public void onNext(Object o) {
                ReplyBaseResultBean bean = (ReplyBaseResultBean) o;
                loadSendData( bean );
            }

            @Override
            public void onErr(Throwable e) {
//                showWarningDialog(R.string.tip_network_error);
            }
        }) );
    }

    /**
     * 处理发送短信数据
     * @param bean
     */
    protected void loadSendData(ReplyBaseResultBean bean ){
        if ( bean == null ){
            return;
        }
        String result = bean.result;


        if (bean.isSuccess()){
            ToastUtil.toast(mContext,"验证码已发送,请注意查收!");
        }else {
            if (!TextUtils.isEmpty(result)){
                ToastUtil.toast(mContext,result);
            }
        }
    }

    /**
     * 短信验证码是否过期
     */
    private void codePast(String phone){
        CodePastParam param = new CodePastParam(phone);
        AppRequestImpl.getInstance( ).codePast(param,new ProgressSubscriber(mContext,getString(R.string.tip_is_code_past), new SubscriberResultListener() {
            @Override
            public void onNext(Object o) {
                CodePastBean bean = (CodePastBean) o;
                loadCodePastData( bean );
            }

            @Override
            public void onErr(Throwable e) {
//                showWarningDialog(R.string.tip_network_error);
            }
        }) );
    }

    /**
     * 处理验证码有效期数据
     * @param bean
     */
    protected void loadCodePastData(CodePastBean bean ){
        if ( bean == null ){
            return;
        }
        boolean past = bean.past;

        if (past){   //验证码有效,前往完善信息的页面
            checkAction(mFlag);
        }else {     //重新请求发送验证码
            initOperation();
        }
    }

}

你可能感兴趣的:(android 中 自己实现验证码表格填写的效果)