自定义EditText,错误校验和提示

在项目开发中多次出现相同的编辑框模块,且样式和要求基本相同,为了让代码利用率更高,跟同事讨论之后对编辑框进行了自定义,这些模块可以单独复用到其他项目,感觉使用起来还不错,现在分享给大家。

自定义的编辑框的基本操作流程,无非就是了解原本编辑框的基本属性和方法,然后进行扩展,从而实现或满足自己的基本需求,这也符合面向对象的要求,继承、封装、多态。下面就直接上代码:

第一步:自定义个基类继承相对应的布局结构

public abstract class EditTextVerifyBase extends RelativeLayout {
    protected EditText inputEt;//输入框
    protected TextView errMsgTv;//错误提示消息
    protected int maxLength;//输入框最大长度
    protected int maxInputLength;
    protected boolean isNeed;//是否必须输入
    protected boolean needCheck;//是否需要校验
    protected boolean couldClear; //是否允许清空输入框
    protected int startCheckLength;//开始字符校验的长度 默认开始7 子类可以调整

    //新增是否需要设置不可编辑
    protected boolean isFocusable = true;
    protected boolean isFocusableInTouchMode = true;

    protected String nullMsg;
    protected String errMsg;
    protected String overLengthMsg;//超过长度时候 报的错误
    protected  String hint;
    protected TextWatcher textWatcher;
    protected ImageView clearBtn;
    protected String releTitle;//关联控件的 text 作为错误信息输出提示
    protected boolean showErrorMsg;//是否需要显示 错误信息

    protected EditTextVerifyInterface editTextVericyInterface;

    public EditTextVerifyBase(Context context, AttributeSet attrs) {
        super(context, attrs);
        startCheckLength = 1;
        LayoutInflater.from(context).inflate(R.layout.edit_text_vericfy_layout,this,true);
        inputEt = (EditText) findViewById(R.id.inputEt);
        errMsgTv = (TextView) findViewById(R.id.errMsgTv);
        clearBtn = (ImageView)findViewById(R.id.clearBtn);
        //属性一栏
        TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.edittextverify);
        showErrorMsg = true;
        if(attributes!=null) {
            isNeed = attributes.getBoolean(R.styleable.edittextverify_isNeed,true);
            needCheck = attributes.getBoolean(R.styleable.edittextverify_needCheck,true);
            maxLength = attributes.getInteger(R.styleable.edittextverify_maxLength,0);
            hint = attributes.getString(R.styleable.edittextverify_hint);
            //默认 可以清空输入框
            couldClear = attributes.getBoolean(R.styleable.edittextverify_couldClear,true);
            releTitle = attributes.getString(R.styleable.edittextverify_releTitle);
            isFocusable = attributes.getBoolean(R.styleable.edittextverify_isFocusable,true);
            isFocusableInTouchMode = attributes.getBoolean(R.styleable.edittextverify_isFocusableInTouchMode,true);
            if(attributes.hasValue(R.styleable.edittextverify_maxInputLength)){
                maxInputLength = attributes.getInt(R.styleable.edittextverify_maxInputLength,0);
                InputFilter[] fa= new InputFilter[1];
                fa[0] = new InputFilter.LengthFilter(maxInputLength);
                inputEt.setFilters(fa);
            }
            if(StringUtils.isEmpty(releTitle)){
                releTitle="";
            }
            if(couldClear){
                clearBtn.setVisibility(VISIBLE);
            }else{
                clearBtn.setVisibility(GONE);
            }
            inputEt.setHint(hint);//设置输入框默认显示
            initEvent();
            inputEt.addTextChangedListener(textWatcher);//添加监听事件

            if(!isFocusable && !isFocusableInTouchMode){
                inputEt.setFocusableInTouchMode(isFocusable);
                inputEt.setFocusable(isFocusableInTouchMode);
            }
        }
    }

    public void initEvent(){
        textWatcher = new 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) {
                if(couldClear){
                    clearBtn.setVisibility(VISIBLE);
                }
                checkError();
                if(editTextVerifyInterface!=null){
                    editTextVerifyInterface.onChangedListener(s,start,before,count);
                }
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        };

        clearBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                inputEt.setText("");//清空输入框
            }
        });
    }
    private boolean checkNull(boolean hasError){
        int visible = VISIBLE;
        if(isNeed && StringUtils.isEmpty(inputEt.getText().toString())&&!hasError){ //校验是否为空
            errMsgTv.setText(nullMsg);
            hasError = true;
            visible = VISIBLE;
        }
        if(hasError) {
            errMsgTv.setVisibility(visible);
        }else{
            errMsgTv.setVisibility(GONE);
        }

        return hasError;
    }

    private boolean checkError(){
        Editable editable = inputEt.getText();
        int visible = VISIBLE;
        boolean hasError = false;
        if(!StringUtils.isEmpty(editable.toString())&&startCheckLength<=editable.length()){
            //需要校验 并且  字符开始校验长度 小于 字符长度
            //长度校验
            String textValue = editable.toString();
            //去除收尾空格空格功能
            if(maxLength!=0&&textValue.trim().length()>maxLength){
                //超过设置的最大长度
                errMsgTv.setText(overLengthMsg);
                visible = VISIBLE;
                hasError = true;
            }else if(needCheck&&!verify(editable.toString())){
                errMsgTv.setText(errMsg);
                visible = VISIBLE;
                hasError = true;
            }else{
                visible = GONE;
                hasError = false;
            }
        }else{
            visible = GONE;
            hasError = false;
        }
        if(hasError) {
            errMsgTv.setVisibility(visible);
        }else{
            errMsgTv.setVisibility(GONE);
        }
        return hasError;
    }
    /**
     * 需要子类 从新定义规则
     * @param text
     * @return
     */
    abstract boolean verify(String text);

    public void setNullMsg(String errMsg){
        this.nullMsg = errMsg;
    }
    public void setErrMsg(String errMsg){
        this.errMsg = errMsg;
    }
    public void setOverLengthMsg(String errMsg){
        this.overLengthMsg = errMsg;
    }

    public boolean getHasError(){
        boolean hasError = checkError();
        hasError = checkNull(hasError);
        return hasError;
    }

    /**
     * 返回去除收尾空格的 文本
     * @return
     */
    public String getText(){
        return this.inputEt.getText().toString().trim();
    }
    public void setText(String text){
        this.inputEt.setText(text);
    }

    /**
     * 是否需要显示错误信息
     * @param showErrorMsg
     */
    public void setShowErrorMsg(boolean showErrorMsg){
        this.showErrorMsg = showErrorMsg;
    }

    public void setOnFocusChangeListener(OnFocusChangeListener onFocusChangeListener){
        inputEt.setOnFocusChangeListener(onFocusChangeListener);
    }
    public int getId(){
        return inputEt.getId();
    }

    public String getReleTitle() {
        return releTitle;
    }

    public int getMaxLength(){
        return maxLength;
    }

    public void setFocusableFalse(){
        inputEt.setFocusable(false);
        inputEt.setFocusableInTouchMode(false);
        clearBtn.setVisibility(View.GONE);
    }

    public void setEditTextVerifyInterface(EditTextVerifyInterface editTextVerifyInterface){
        this.editTextVerifyInterface = editTextVerifyInterface;
    }

    public interface EditTextVerifyInterface{
        void onChangedListener(CharSequence s, int start, int before, int count);
    }

}

对应的布局

<?xml version="1.0" encoding="utf-8"?>
<!-- 自定义校验 textView -->
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    //输入框
    <EditText
        android:id="@+id/inputEt"
        android:layout_width="match_parent"
        android:layout_height="25dp"
        android:layout_marginRight="40dp"
        android:background="@null"
        android:textSize="@dimen/edit_second_paragraph"
        android:layout_weight="8"
        android:paddingLeft="5px"
        android:layout_alignParentTop="true"
        android:textCursorDrawable="@drawable/et_cursor"
        android:textColor="@color/Grey2"
        android:textColorHint="@color/Grey3" />
    //右边清除按钮
    <ImageView
        android:id="@+id/clearBtn"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_gravity="center"
        android:visibility="visible"
        android:layout_alignParentRight="true"
        android:paddingRight="5dp"
        android:paddingLeft="5px"
        android:src="@drawable/poor"
        />
    //错误提示语
    <TextView
        android:id="@+id/errMsgTv"
        android:layout_below="@+id/inputEt"
        android:layout_alignParentLeft="true"
        android:layout_alignLeft="@+id/inputEt"
        android:layout_alignRight="@+id/inputEt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:textColor="@color/Red"
        android:textSize="@dimen/text_error_word"
        android:padding="6px"
        android:visibility="gone"/>
</RelativeLayout>

对应的style

 <declare-styleable name="edittextverify">
        <attr name="hint" format="string" /> <!-- 输入框 hint 显示 -->
        <attr name="isNeed" format="boolean" /> <!-- 是否必输入 默认必须 -->
        <attr name="needCheck" format="boolean" /> <!-- 是否需要校验 默认必须 -->
        <attr name="maxLength" format="integer" />  <!--输入框 长度 0 不校验长度 默认0 -->
        <attr name="couldClear" format="boolean" />
        <attr name="maxInputLength" format="integer" />
        <attr name="releTitle" format="string"/>
        <attr name="isFocusable" format="boolean"/><!--输入框是否获得焦点 -->
        <attr name="isFocusableInTouchMode" format="boolean"/>
    </declare-styleable>

第二步:对第一步方法继承封装(举出一个项目中的实例,其余的根据个人需求进行封装改造)
银行卡的应用包含卡号以及合法性的校验(如下是需要用到的代码,其余就不粘贴了)

public class EditTextVerifyBankCard extends EditTextVerifyBase {
    public EditTextVerifyBankCard(Context context, AttributeSet attrs) {
        super(context, attrs);
        setErrMsg(releTitle+"格式错误");
        setNullMsg(releTitle+"不能为空");
        setOverLengthMsg(releTitle+"不能超过"+maxLength+"位");
    }

    @Override
    boolean verify(String text) {
        return ToolsUtil.checkBankCard(text);
    }
}

下面的代码是封装在ToolsUtil一个综合工具包下面,便于管理

/**
     * 校验银行卡卡号
     *
     * @param cardId
     * @return
     */
    public static boolean checkBankCard(String cardId) {
        if (StringUtils.isEmpty(cardId)) {
            return false;
        }
        char bit = getBankCardCheckCode(cardId
                .substring(0, cardId.length() - 1));
        if (bit == 'N') {
            return false;
        }
        return cardId.charAt(cardId.length() - 1) == bit;
    }

    /**
     * 从不含校验位的银行卡卡号采用 Luhm 校验算法获得校验位
     *
     * @param nonCheckCodeCardId
     * @return
     */
    public static char getBankCardCheckCode(String nonCheckCodeCardId) {
        if (nonCheckCodeCardId == null
                || nonCheckCodeCardId.trim().length() == 0
                || !nonCheckCodeCardId.matches("\\d+")) {
            // 如果传的不是数据返回N
            return 'N';
        }
        char[] chs = nonCheckCodeCardId.trim().toCharArray();
        int luhmSum = 0;
        for (int i = chs.length - 1, j = 0; i >= 0; i--, j++) {
            int k = chs[i] - '0';
            if (j % 2 == 0) {
                k *= 2;
                k = k / 10 + k % 10;
            }
            luhmSum += k;
        }
        return (luhmSum % 10 == 0) ? '0' : (char) ((10 - luhmSum % 10) + '0');
    }

第三步:准备工作整理好之后,开始应用到实际布局

 <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:layout_gravity="center">

        <TextView
            android:id="@+id/tv_bankCardShow"
            android:layout_width="110dp"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="15dp"
            android:text="银行卡号"
            android:textColor="@color/Black"
            android:textSize="@dimen/edit_second_paragraph" />

        <包名路径.EditText.EditTextVerifyBankCard
            android:id="@+id/et_bank_num"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="5dp"
            android:layout_toLeftOf="@+id/iv_bank_pic"
            android:layout_toRightOf="@+id/tv_bankCardShow"
            android:gravity="left"
            etv:hint="请输入银行卡号"
            etv:isNeed="true"
            etv:maxLength="30"
            etv:needCheck="true"
            etv:couldClear = "false"
            etv:releTitle="常用银行卡号" />
        <ImageView
            android:id="@+id/iv_bank_pic"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="5dp"
            android:src="@drawable/camera_upload"
            android:visibility="visible" />

    </RelativeLayout>

第四步:在类里面的操作判断,在失去焦点之后会进行相应的校验

EditTextVerifyBankCard etBankNum = getViewById(R.id.et_bank_num);//银行卡号码
 if (etBankNum.getHasError()){
         return ;
        }

上述方法还可以用在姓名、身份证、手机号等等方面的校验,需要自己根据需求进行封装,欢迎各路大神进行批评指点,记得留言呦

猜你喜欢

转载自blog.csdn.net/boomlei/article/details/80857059
今日推荐