EditText实现分割输入内容

本文介绍如何通过自定义EditText控件,实现手机号、银行卡号和身份证号等输入内容的分割显示,提升用户体验。提供属性配置及代码使用说明,并附带效果展示和项目源码链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在项目中可能会有许多需要输入手机号码、银行卡号或者身份证号等内容的输入框。如果直接输入的话将会是一堆号码堆在一起,第一是不太美观,第二也容易出错,用户体验不太好。但是若将输入的号码按特定格式进行分割将会大大提高用户体验!

以下是对常用的号码进行简单封装的自定义输入框控件,方便我们在开发过程中使用:

  1. 该控件支持xml属性指定,也支持代码指定;
  2. 该控件支持类型分别为电话号码(000 0000 0000)、银行卡号(0000 0000 0000 0000 000)和身份证号(000000 0000 0000 0000)

效果图

320*568

自定义EditText

/**
 * @Description 分割输入框
 * @Author 一花一世界
 */
public class ContentWithSpaceEditText extends EditText {

    public static final int TYPE_PHONE = 0;
    public static final int TYPE_CARD = 1;
    public static final int TYPE_IDCARD = 2;
    private int maxLength = 100;
    private int contentType;
    private int start, count, before;
    private String digits;

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

    public ContentWithSpaceEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        parseAttributeSet(context, attrs);
    }

    public ContentWithSpaceEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        parseAttributeSet(context, attrs);
    }

    private void parseAttributeSet(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ContentWithSpaceEditText, 0, 0);
        contentType = ta.getInt(R.styleable.ContentWithSpaceEditText_type, 0);
        ta.recycle();
        initType();
        setSingleLine();
        addTextChangedListener(watcher);
    }

    private void initType() {
        if (contentType == TYPE_PHONE) {
            maxLength = 13;
            digits = "0123456789 ";
            setInputType(InputType.TYPE_CLASS_NUMBER);
        } else if (contentType == TYPE_CARD) {
            maxLength = 23;
            digits = "0123456789 ";
            setInputType(InputType.TYPE_CLASS_NUMBER);
        } else if (contentType == TYPE_IDCARD) {
            maxLength = 21;
            digits = "0123456789xX ";
            setInputType(InputType.TYPE_CLASS_TEXT);
        }
        setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
    }

    @Override
    public void setInputType(int type) {
        super.setInputType(type);
        // setKeyListener要在setInputType后面调用,否则无效
        if (!TextUtils.isEmpty(digits)) {
            setKeyListener(DigitsKeyListener.getInstance(digits));
        }
    }

    private TextWatcher watcher = 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) {
            ContentWithSpaceEditText.this.start = start;
            ContentWithSpaceEditText.this.before = before;
            ContentWithSpaceEditText.this.count = count;
        }

        @Override
        public void afterTextChanged(Editable s) {
            if (s == null) {
                return;
            }
            //判断是否是在中间输入,需要重新计算
            boolean isMiddle = (start + count) < (s.length());
            //在末尾输入时,是否需要加入空格
            boolean isNeedSpace = false;
            if (!isMiddle && isSpace(s.length())) {
                isNeedSpace = true;
            }
            if (isMiddle || isNeedSpace || count > 1) {
                String newStr = s.toString();
                newStr = newStr.replace(" ", "");
                StringBuilder sb = new StringBuilder();
                int spaceCount = 0;
                for (int i = 0; i < newStr.length(); i++) {
                    sb.append(newStr.substring(i, i + 1));
                    //如果当前输入的字符下一位为空格(i+1+1+spaceCount),因为i是从0开始计算的,所以一开始的时候需要先加1
                    if (isSpace(i + 2 + spaceCount)) {
                        sb.append(" ");
                        spaceCount += 1;
                    }
                }
                removeTextChangedListener(watcher);
                s.replace(0, s.length(), sb);
                //如果是在末尾的话,或者加入的字符个数大于零的话(输入或者粘贴)
                if (!isMiddle || count > 1) {
                    setSelection(s.length() <= maxLength ? s.length() : maxLength);
                } else if (isMiddle) {
                    //如果是删除
                    if (count == 0) {
                        //如果删除时,光标停留在空格的前面,光标则要往前移一位
                        if (isSpace(start - before + 1)) {
                            setSelection((start - before) > 0 ? start - before : 0);
                        } else {
                            setSelection((start - before + 1) > s.length() ? s.length() : (start - before + 1));
                        }
                    }
                    //如果是增加
                    else {
                        if (isSpace(start - before + count)) {
                            setSelection((start + count - before + 1) < s.length() ? (start + count - before + 1) : s.length());
                        } else {
                            setSelection(start + count - before);
                        }
                    }
                }
                addTextChangedListener(watcher);
            }
        }
    };

    private boolean isSpace(int length) {
        if (contentType == TYPE_PHONE) {
            return isSpacePhone(length);
        } else if (contentType == TYPE_CARD) {
            return isSpaceCard(length);
        } else if (contentType == TYPE_IDCARD) {
            return isSpaceIDCard(length);
        }
        return false;
    }

    private boolean isSpacePhone(int length) {
        return length >= 4 && (length == 4 || (length + 1) % 5 == 0);
    }

    private boolean isSpaceCard(int length) {
        return length % 5 == 0;
    }

    private boolean isSpaceIDCard(int length) {
        return length > 6 && (length == 7 || (length - 2) % 5 == 0);
    }

    public void setContentType(int contentType) {
        this.contentType = contentType;
        initType();
    }

    public String getTextWithoutSpace() {
        return super.getText().toString().replace(" ", "");
    }

    /**
     * @Description 内容校验
     */
    public boolean isContentCheck() {
        String text = getTextWithoutSpace();
        if (contentType == TYPE_PHONE) {
            if (TextUtils.isEmpty(text)) {
                ToastUtil.showText(UIUtils.getString(R.string.phone_number_not_empty));
            } else if (text.length() < 11) {
                ToastUtil.showText(UIUtils.getString(R.string.phone_number_incorrect_length));
            } else {
                return true;
            }
        } else if (contentType == TYPE_CARD) {
            if (TextUtils.isEmpty(text)) {
                ToastUtil.showText(UIUtils.getString(R.string.bank_card_not_empty));
            } else if (text.length() < 16) {
                ToastUtil.showText(UIUtils.getString(R.string.bank_card_incorrect_length));
            } else {
                return true;
            }
        } else if (contentType == TYPE_IDCARD) {
            if (TextUtils.isEmpty(text)) {
                ToastUtil.showText(UIUtils.getString(R.string.identity_number_not_empty));
            } else if (text.length() < 18) {
                ToastUtil.showText(UIUtils.getString(R.string.identity_number_incorrect_length));
            } else {
                return true;
            }
        }
        return false;
    }
}

配置attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ContentWithSpaceEditText">
        <attr name="type" format="enum">
            <enum name="phone" value="0" />
            <enum name="card" value="1" />
            <enum name="IDCard" value="2" />
        </attr>
    </declare-styleable>
</resources>

布局文件中使用

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/theme_bg"
    android:orientation="vertical">

    <com.wiggins.splitinput.widget.TitleView
        android:id="@+id/titleView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/item_normal"
        android:layout_margin="@dimen/margin_normal"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <TextView
            android:layout_width="@dimen/btn_width_normal"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:text="@string/phone_number"
            android:textColor="@color/blue"
            android:textSize="@dimen/font_normal" />

        <com.wiggins.splitinput.widget.ContentWithSpaceEditText
            android:id="@+id/edt_phone_input"
            android:layout_width="match_parent"
            android:layout_height="@dimen/item_normal"
            android:background="@color/white"
            android:gravity="center"
            android:hint="@string/please_enter_content"
            android:inputType="number"
            android:textColor="@color/blue"
            android:textColorHint="@color/gray"
            android:textCursorDrawable="@null"
            android:textSize="@dimen/font_normal"
            app:type="phone" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/item_normal"
        android:layout_margin="@dimen/margin_normal"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <TextView
            android:layout_width="@dimen/btn_width_normal"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:text="@string/bank_card_number"
            android:textColor="@color/blue"
            android:textSize="@dimen/font_normal" />

        <com.wiggins.splitinput.widget.ContentWithSpaceEditText
            android:id="@+id/edt_bank_card_input"
            android:layout_width="match_parent"
            android:layout_height="@dimen/item_normal"
            android:background="@color/white"
            android:gravity="center"
            android:hint="@string/please_enter_content"
            android:inputType="number"
            android:textColor="@color/blue"
            android:textColorHint="@color/gray"
            android:textCursorDrawable="@null"
            android:textSize="@dimen/font_normal"
            app:type="card" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/item_normal"
        android:layout_margin="@dimen/margin_normal"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <TextView
            android:layout_width="@dimen/btn_width_normal"
            android:layout_height="match_parent"
            android:gravity="center_vertical"
            android:text="@string/identity_number"
            android:textColor="@color/blue"
            android:textSize="@dimen/font_normal" />

        <com.wiggins.splitinput.widget.ContentWithSpaceEditText
            android:id="@+id/edt_identity_input"
            android:layout_width="match_parent"
            android:layout_height="@dimen/item_normal"
            android:background="@color/white"
            android:gravity="center"
            android:hint="@string/please_enter_content"
            android:inputType="number"
            android:textColor="@color/blue"
            android:textColorHint="@color/gray"
            android:textCursorDrawable="@null"
            android:textSize="@dimen/font_normal"
            app:type="IDCard" />
    </LinearLayout>
</LinearLayout>

项目地址 ☞ 传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值