原创首发
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
原创发布于
阳光沙滩
,未经作者授权,禁止转载