Android实战场景 - 输入手机号、银行卡号、身份证号时动态格式化

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

在日常项目开发中如果稍微严谨点的话其中关于手机号、银行卡号、身份证号的输入格式有做了限制格式化操作主要是为了给用户带来更好的体验感
最近同事正好问到了我这个问题虽然以前做过这类型功能但是并未记录所以我就去网上扒了扒特此记录一下~

可能对你有所帮助的Blog

功能来源需求体验来自用户提升来自自我 Striving ...

基础了解

首先我需要做的功能针对EditText控件要满足以下几点

  • 当用户输入手机号、银行卡号、身份证号时动态加入空格
  • 当用户删除数据时逐步清理空格
  • 当传递数据时数据中不含空格

关于在Andoird中想要实时监听文本变化一般都需要加 addTextChangedListener 监听者不了解这部分的可以去看看 监听EditText的文本变化

在这里插入图片描述


功能实现

使用方式

MainActivity

package com.example.edittextdemo

import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var etPhone = findViewById<EditText>(R.id.et_phone)
        var etBank = findViewById<EditText>(R.id.et_bank)
        var etCard = findViewById<EditText>(R.id.et_card)

        //手机号一般是11位
        var phoneTextWatcher = FormatTextWatcher(etPhone, 11 + 2)
        phoneTextWatcher.setSpaceType(FormatTextWatcher.SpaceType.mobilePhoneNumberType)

        //银行卡储蓄卡卡号有三种格式19位、17位和16位的; 信用卡卡号则统一为16位号码~
        var bankTextWatcher = FormatTextWatcher(etBank, 19 + 4)
        bankTextWatcher.setSpaceType(FormatTextWatcher.SpaceType.bankCardNumberType)

        //身份证号一般是18位
        var cardTextWatcher = FormatTextWatcher(etCard, 18 + 2)
        cardTextWatcher.setSpaceType(FormatTextWatcher.SpaceType.IDCardNumberType)

        //获取输入数据
        var mBtn = findViewById<Button>(R.id.btn)
        mBtn.setOnClickListener {
            var phoneContent = phoneTextWatcher.textNotSpace
            var bankContent = bankTextWatcher.textNotSpace
            var cardContent = cardTextWatcher.textNotSpace
            Toast.makeText(this, "手机号$phoneContent & 银行卡号$bankContent & 身份证号$cardContent", Toast.LENGTH_SHORT).show()
        }
    }
}

activity_main

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/et_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:digits="0123456789 X"
        android:hint="输入您的手机号"
        android:inputType="numberSigned"
        android:textSize="20dp"
        tools:ignore="MissingConstraints" />

    <EditText
        android:id="@+id/et_bank"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:digits="0123456789 X"
        android:hint="输入您的银行卡"
        android:inputType="numberSigned"
        android:textSize="20dp"
        tools:ignore="MissingConstraints" />

    <EditText
        android:id="@+id/et_card"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:digits="0123456789 X"
        android:hint="输入您的身份证号"
        android:inputType="numberSigned"
        android:textSize="20dp"
        tools:ignore="MissingConstraints" />

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="获取输入数据" />
</androidx.appcompat.widget.LinearLayoutCompat>

实现过程

我翻了下网上资源发现基本都是以下这份自定义的 TextWatcher监听 处理 所以我直接就拿过来跑Demo了

因为我需要的身份证格式 1442202 19980201 1277 而其原始格式为 1442202 1998 0201 1277 所以我稍作了下修改

FormatTextWatcher

package com.example.edittextdemo;

import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.widget.EditText;

/**
 * 输入银行卡、手机、身份证格式化过滤器
 */
public class FormatTextWatcher implements TextWatcher {
    /**
     * text改变之前的长度
     */
    private int beforeTextLength = 0;
    private int onTextLength = 0;
    private boolean isChanged = false;
    private StringBuffer buffer = new StringBuffer();
    /**
     * 改变之前text空格数量
     */
    int spaceNumberA = 0;
    private EditText editText;
    /**
     * text最大长度限制
     */
    private int maxLength;
    private SpaceType spaceType;
    /**
     * 记录光标的位置
     */
    private int location = 0;
    /**
     * 是否是主动设置text
     */
    private boolean isSetText = false;

    public FormatTextWatcher(EditText editText, int maxLength) {
        this.editText = editText;
        this.maxLength = maxLength;
        if (editText == null) {
            new NullPointerException("editText is null");
        }
        spaceType = SpaceType.defaultType;
        editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(
                maxLength)});
        editText.addTextChangedListener(this);
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count,int after) {
        beforeTextLength = s.length();
        if (buffer.length() > 0) {
            buffer.delete(0, buffer.length());
        }
        spaceNumberA = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' ') {
                spaceNumberA++;
            }
        }
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        onTextLength = s.length();
        buffer.append(s.toString());
        if (onTextLength == beforeTextLength || onTextLength > maxLength
                || isChanged) {
            isChanged = false;
            return;
        }
        isChanged = true;
    }

    @Override
    public void afterTextChanged(Editable s) {
        if (isChanged) {
            location = editText.getSelectionEnd();
            int index = 0;
            while (index < buffer.length()) { // 删掉所有空格
                if (buffer.charAt(index) == ' ') {
                    buffer.deleteCharAt(index);
                } else {
                    index++;
                }
            }

            index = 0;
            int spaceNumberB = 0;
            while (index < buffer.length()) { // 插入所有空格
                spaceNumberB = insertSpace(index, spaceNumberB);
                index++;
            }

            String str = buffer.toString();

            // 下面是计算光位置的
            if (spaceNumberB > spaceNumberA) {
                location += (spaceNumberB - spaceNumberA);
                spaceNumberA = spaceNumberB;
            }
            if (isSetText) {
                location = str.length();
                isSetText = false;
            } else if (location > str.length()) {
                location = str.length();
            } else if (location < 0) {
                location = 0;
            }
            updateContext(s, str);
            isChanged = false;
        }
    }

    /**
     * 更新编辑框中的内容
     *
     * @param editable
     * @param values
     */
    private void updateContext(Editable editable, String values) {
        if (spaceType == SpaceType.IDCardNumberType) {
            editable.replace(0, editable.length(), values);
        } else {
            editText.setText(values);
            try {
                editText.setSelection(location);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据类型插入空格
     *
     * @param index
     * @param spaceNumberAfter
     * @return
     * @see [类、类#方法、类#成员]
     */
    private int insertSpace(int index, int spaceNumberAfter) {
        switch (spaceType) {
            // 相隔四位空格
            case mobilePhoneNumberType:
                if (index == 3 || ((index > 7) && ((index - 3) % (4 * spaceNumberAfter) == spaceNumberAfter))) {
                    buffer.insert(index, ' ');
                    spaceNumberAfter++;
                }
                break;
            case IDCardNumberType:
             /*   if (index == 6
                        || ((index > 10) && ((index - 6) % (4 * spaceNumberAfter) == spaceNumberAfter))) {
                    buffer.insert(index, ' ');
                    spaceNumberAfter++;
                }*/
                if (index == 6
                        || ((index > 14) && ((index - 6) % (4 * spaceNumberAfter) == spaceNumberAfter))) {
                    buffer.insert(index, ' ');
                    spaceNumberAfter++;
                }
                break;
            default:
                if (index > 3
                        && (index % (4 * (spaceNumberAfter + 1)) == spaceNumberAfter)) {
                    buffer.insert(index, ' ');
                    spaceNumberAfter++;
                }
                break;
        }
        return spaceNumberAfter;
    }

    /***
     * 计算需要的空格数
     *
     * @return 返回添加空格后的字符串长度
     * @see [类、类#方法、类#成员]
     */
    private int computeSpaceCount(CharSequence charSequence) {
        buffer.delete(0, buffer.length());
        buffer.append(charSequence.toString());
        int index = 0;
        int spaceNumberB = 0;
        while (index < buffer.length()) { // 插入所有空格
            spaceNumberB = insertSpace(index, spaceNumberB);
            index++;
        }
        buffer.delete(0, buffer.length());
        return index;
    }

    /**
     * 设置空格类型
     *
     * @param spaceType
     * @see [类、类#方法、类#成员]
     */
    public void setSpaceType(SpaceType spaceType) {
        this.spaceType = spaceType;
    }

    /**
     * 设置输入字符
     *
     * @param charSequence
     * @return 返回设置成功失败
     * @see [类、类#方法、类#成员]
     */
    public boolean setText(CharSequence charSequence) {
        if (editText != null && !TextUtils.isEmpty(charSequence) && computeSpaceCount(charSequence) <= maxLength) {
            isSetText = true;
            editText.removeTextChangedListener(this);
            editText.setText(charSequence);
            editText.addTextChangedListener(this);
            return true;
        }
        return false;
    }

    /**
     * 得到输入的字符串去空格后的字符串
     *
     * @return
     * @see [类、类#方法、类#成员]
     */
    public String getTextNotSpace() {
        if (editText != null) {
            return delSpace(editText.getText().toString());
        }
        return null;
    }

    /**
     * 得到输入的字符串去空格后的长度
     *
     * @return
     * @see [类、类#方法、类#成员]
     */
    public int getLengthNotSpace() {
        if (editText != null) {
            return getTextNotSpace().length();
        }
        return 0;
    }

    /**
     * 得到空格数量
     *
     * @return
     * @see [类、类#方法、类#成员]
     */
    public int getSpaceCount() {
        return spaceNumberA;
    }

    /**
     * 去掉字符空格换行符等
     *
     * @param str
     * @return
     * @see [类、类#方法、类#成员]
     */
    private String delSpace(String str) {
        if (str != null) {
            str = str.replaceAll("\r", "");
            str = str.replaceAll("\n", "");
            str = str.replace(" ", "");
        }
        return str;
    }

    /**
     * 空格类型
     *
     * @author 江钰锋 0152
     * @version [版本号, 2015年4月21日]
     * @see [相关类/方法]
     * @since [产品/模块版本]
     */
    public enum SpaceType {
        /**
         * 默认类型
         */
        defaultType,
        /**
         * 银行卡类型
         */
        bankCardNumberType,
        /**
         * 手机号类型
         */
        mobilePhoneNumberType,
        /**
         * 身份证类型
         */
        IDCardNumberType
    }

}

如果需要修改添加空格的位置可以自行更改内部规则

在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: android