原创首发    
 EditText限制输入2位小数功能完善

前言
事情的过程是这样的,康师傅写的EditText限制输入2位小数这篇文章中实现的功能有点BUG,断点同学发现有BUG并提出了问题Android edittext 显示2位小数输入的正则请教,于是乎我开始解决这个问题,所以有了这篇文章。
放码过来
这里只放了Activity的代码,布局就一个EditText,就不放布局代码了。
Java版本代码
NumActivity.java
import android.annotation.SuppressLint;
import android.text.InputFilter;
import android.text.Spanned;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.widget.EditText;
import android.widget.TextView;
import com.example.firstlinecode.R;
import com.example.firstlinecode.base.BaseActivity;
import java.lang.reflect.Field;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NumActivity extends BaseActivity {
    private static final String TAG = "NumActivity";
    private EditText mEtInputText;
    @Override
    public int getLayoutResId() {
        return R.layout.activity_num;
    }
    @Override
    public void initView() {
        mEtInputText = findViewById(R.id.etInputText);
        disableCopyAndPaste(mEtInputText);
    }
    @Override
    public void initEvent() {
        mEtInputText.setFilters(new InputFilter[]{new PriseNumberFilter()});
    }
    private static class PriseNumberFilter implements InputFilter {
        private final Pattern mPattern;
        public PriseNumberFilter() {
            //前面位数不限,后面限制2位,如果需要自行修改即可
            String regex = "[0-9]*+(\\.[0-9]{0,2})?";
            mPattern = Pattern.compile(regex);
        }
        /**
         * @param source 将要输入的字符串,如果是删除操作则为空字符串
         * @param start  将要输入的字符串起始下标,一般为0
         * @param end    start + source字符的长度
         * @param dest   输入之前文本框中的内容
         * @param dstart 将会被替换的起始位置
         * @param dend   将会被替换的字符串长度
         * @return 返回值 :方法返回的值将会替换掉dest字符串中dstartd位置到dend位置之间字符
         * 返回source表示不做任何处理,返回空字符串""表示不输入任何字符
         */
        @Override
        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
            if (".".contentEquals(source) && "".contentEquals(dest.toString())) {
                return "0.";
            }
            StringBuilder builder = new StringBuilder(dest);
            if ("".contentEquals(source)) {
                builder.replace(dstart, dend, "");
            } else {
                builder.insert(dstart, source);
            }
            String resultTemp = builder.toString();
            Matcher matcher = mPattern.matcher(resultTemp);
            // 新添加的代码片段 resultTemp.matches("^0\\d") ||
            if (resultTemp.matches("^0\\d") || !matcher.matches()) {
                return "";
            }
            return null;
        }
    }
    @SuppressLint("ClickableViewAccessibility")
    public void disableCopyAndPaste(final EditText editText) {
        try {
            if (editText == null) {
                return;
            }
            // ========新添加的代码start=======
            editText.setOnClickListener(v -> {
                editText.requestFocus();
                editText.setSelection(editText.getText().length());
            });
            // ========新添加的代码end=======
            editText.setOnLongClickListener(v -> true);
            editText.setLongClickable(false);
            editText.setOnTouchListener((v, event) -> {
                // ========新添加的代码start=======
                editText.requestFocus();
                editText.setSelection(editText.getText().length());
                // ========新添加的代码end=======
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    // setInsertionDisabled when user touches the view
                    setInsertionDisabled(editText);
                }
                return false;
            });
            editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
                @Override
                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                    return false;
                }
                @Override
                public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                    return false;
                }
                @Override
                public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                    return false;
                }
                @Override
                public void onDestroyActionMode(ActionMode mode) {
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    @SuppressLint("PrivateApi")
    private void setInsertionDisabled(EditText editText) {
        try {
            Field editorField = TextView.class.getDeclaredField("mEditor");
            editorField.setAccessible(true);
            Object editorObject = editorField.get(editText);
            // if this view supports insertion handles
            Class editorClass = Class.forName("android.widget.Editor");
            Field mInsertionControllerEnabledField = editorClass.getDeclaredField("mInsertionControllerEnabled");
            mInsertionControllerEnabledField.setAccessible(true);
            mInsertionControllerEnabledField.set(editorObject, false);
            // if this view supports selection handles
            Field mSelectionControllerEnabledField = editorClass.getDeclaredField("mSelectionControllerEnabled");
            mSelectionControllerEnabledField.setAccessible(true);
            mSelectionControllerEnabledField.set(editorObject, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Kotlin版本代码
NumActivity.kt
import android.annotation.SuppressLint
import android.text.InputFilter
import android.text.Spanned
import android.view.*
import android.widget.EditText
import android.widget.TextView
import com.example.firstlinecode.R
import com.example.firstlinecode.base.BaseActivity
import kotlinx.android.synthetic.main.activity_num.*
import java.util.regex.Pattern
class NumActivity : BaseActivity() {
    override fun getLayoutResId() = R.layout.activity_num
    override fun initView() {
        disableCopyAndPaste(etInputText)
    }
    override fun initEvent() {
        etInputText.filters = arrayOf(PriseNumberFilter())
    }
    private class PriseNumberFilter : InputFilter {
        private val mPattern: Pattern
        init {
            //前面位数不限,后面限制2位,如果需要自行修改即可
            val regex = "[0-9]*+(\\.[0-9]{0,2})?"
            mPattern = Pattern.compile(regex)
        }
        /**
         * @param source 将要输入的字符串,如果是删除操作则为空字符串
         * @param start  将要输入的字符串起始下标,一般为0
         * @param end    start + source字符的长度
         * @param dest   输入之前文本框中的内容
         * @param dstart 将会被替换的起始位置
         * @param dend   将会被替换的字符串长度
         * @return 返回值 :方法返回的值将会替换掉dest字符串中dstartd位置到dend位置之间字符
         * 返回source表示不做任何处理,返回空字符串""表示不输入任何字符
         */
        override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
            if ("." == source && "" == dest.toString()) {
                return "0."
            }
            val builder = StringBuilder(dest)
            if ("" == source) {
                builder.replace(dstart, dend, "")
            } else {
                builder.insert(dstart, source)
            }
            val resultTemp = builder.toString()
            val matcher = mPattern.matcher(resultTemp)
            return if (Regex("^0\\d") matches resultTemp || !matcher.matches()) {
                ""
            } else null
        }
    }
    @SuppressLint("ClickableViewAccessibility")
    fun disableCopyAndPaste(editText: EditText?) {
        editText?.run {
            try {
                setOnClickListener {
                    requestFocus()
                    setSelection(text.length)
                }
                setOnLongClickListener { true }
                isLongClickable = false
                setOnTouchListener { _: View?, event: MotionEvent ->
                    requestFocus()
                    setSelection(text.length)
                    if (event.action == MotionEvent.ACTION_DOWN) {
                        // setInsertionDisabled when user touches the view
                        setInsertionDisabled(this)
                    }
                    false
                }
                customSelectionActionModeCallback = object : ActionMode.Callback {
                    override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
                        return false
                    }
                    override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
                        return false
                    }
                    override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
                        return false
                    }
                    override fun onDestroyActionMode(mode: ActionMode) {}
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
    @SuppressLint("PrivateApi")
    private fun setInsertionDisabled(editText: EditText) {
        try {
            val editorField = TextView::class.java.getDeclaredField("mEditor")
            editorField.isAccessible = true
            val editorObject = editorField[editText]
            // if this view supports insertion handles
            val editorClass = Class.forName("android.widget.Editor")
            val mInsertionControllerEnabledField = editorClass.getDeclaredField("mInsertionControllerEnabled")
            mInsertionControllerEnabledField.isAccessible = true
            mInsertionControllerEnabledField[editorObject] = false
            // if this view supports selection handles
            val mSelectionControllerEnabledField = editorClass.getDeclaredField("mSelectionControllerEnabled")
            mSelectionControllerEnabledField.isAccessible = true
            mSelectionControllerEnabledField[editorObject] = false
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}
BaseActivity部分的代码
BaseActivity.kt
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.annotation.CallSuper
import androidx.appcompat.app.AppCompatActivity
abstract class BaseActivity : AppCompatActivity() {
    @CallSuper
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val layoutResId = getLayoutResId()
        if (layoutResId > 0) setContentView(layoutResId)
        initView()
        initData()
        initEvent()
    }
    abstract fun getLayoutResId(): Int
    open fun initEvent() {
    }
    open fun initData() {
    }
    open fun initView() {
    }
    override fun onDestroy() {
        super.onDestroy()
        release()
    }
    open fun release() {
    }
    fun startActivity(context: Context, cls: Class<*>) {
        context.startActivity(Intent(context, cls))
    }
    fun startActivity(cls: Class<*>) {
        startActivity(Intent(this, cls))
    }
}
总结
部分输入法有改变光标的功能,这就导致我们在点击和触摸监听器中改变光标到字符串末尾失效。 这部分就需要自己自定义数字键盘实现才能解决啦(我目前还没发现其他方法,如果有的话,麻烦大家评论区告诉我~),自定义数字键盘部分请同学们去看康师傅的 Android开发自定义控件系列课程 啦~
参考文章
请同学们点赞、评论、打赏+关注啦~
          本文由
          A lonely cat
          原创发布于
          阳光沙滩
          ,未经作者授权,禁止转载
        
 

 A lonely cat  回复 @断点-含光君
 A lonely cat  回复 @断点-含光君 



























