详解EditText输入监听TextWatcher

article/2025/10/28 5:22:52

日常开发中,我们可能会遇到需要监听EditText输入,比如判断输入是否为电话号码,获取输入的数据长度来限定字数等。这就需要监听EditText的输入状态。EditText使用TextWatcher实现类似按钮监听事件:

使用方法
效果图:
这里写图片描述

MainActivity.java

public class MainActivity extends AppCompatActivity {private EditText mNumber;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mNumber = (EditText) findViewById(R.id.phone_number);//为EditText设置监听,注意监听类型为TextWatchermNumber.addTextChangedListener(new TextWatcher() {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {toast("您输入的数据为:"+s.toString());}@Overridepublic void afterTextChanged(Editable s) {}});}private void toast(String s){Toast.makeText(getApplication(),s,Toast.LENGTH_SHORT).show();}
}

Activity_main.xml

<?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"xmlns:tools="http://schemas.android.com/tools"android:padding="10dp"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.alphathink.myapplication.MainActivity"><EditText
        android:id="@+id/phone_number"android:layout_width="368dp"android:layout_height="wrap_content"android:hint="@string/number"android:inputType="number"tools:layout_editor_absoluteY="0dp"tools:layout_editor_absoluteX="8dp" /></LinearLayout>

TextWatcher详解

注意:
CharSequence是一个接口,比较常见的String、StringBuilder、StringBuffer都实现了这个接口。当我们看到一个API里面有CharSequence的时候,它也是可以被其子类代替的,一般用String代替即可。

我们看到TextWatcher监听里覆写了3个方法:

     void beforeTextChanged(CharSequence s, int start, int count, int after);   void onTextChanged(CharSequence s, int start, int before, int count);  void afterTextChanged(Editable s); 

执行顺序来说是:beforeTextChanged()>>>onTextChanged()>>>afterTextChanged()

请勿在afterTextChanged();内加入代码进行验证,这个会进入死循环的,后边讲原因。


关于beforeTextChanged(CharSequence s, int start, int count, int after);

官方文档解释:

void beforeTextChanged (CharSequence s, int start, int count, int after)This method is called to notify you that, within s, the count characters beginning at start are about to be replaced by new text with length after. It is an error to attempt to make changes to s from this callback. 意思大概是:
这个方法用来通知你,在字符串s里,光标start开始处的count个字符将要被after长的字符代替,禁止在这个回调里改字符串s。可以理解成提醒你你做了什么操作。
操作》》》系统提醒你做了什么事(还没做)》》》》系统开始做
@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {toast("s字符串为:"+s+"开始处:"+start+",替换体的长度:"+count+"后替换体长度"+after);}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}@Overridepublic void afterTextChanged(Editable s) {}

分别输入一个字符、二个字符、删除一个字符的截图

这里写图片描述 这里写图片描述 这里写图片描述
这样理解:

这个方法执行获取的状态是在你输入前光标所在位置:
输入一个字符:s字符串也就是当前EditText中的内容为空,所以输出空;开始处:0,司空见惯,从0计数;替换长度:0,这个怎么理解呢?理解成将被替换长度即可,你要在光标处往后替换然而往后并没有字符;后替换体长度:1,虽然他光标没动,这里表示他将要改变的长度,等于你输入字符长度1。输入二个字符:s字符串是什么?我们刚刚输入的你字是不是还在,这就是s的内容;开始处:光标所在位置1;替换长度:0,为什么还是0?实话说这个是一直为0的,因为在你输入前执行,你所做的操作时一直往后增加,而增加操作相当于用输入字符替换了一个长度为0的字符,也就是空,所以一直为0;后替换体长度:等于替换字符长度。删除一个字符:删除前执行,所以内容为“你你好”;开始处:2,光标所在位;替换体长度:1,这个这里就变了,为什么变了呢?因为我们做了删除操作,被替换的长度是删除的那个字符长度也就是1;后替换体的长度:0,这个又是一直为0的,为什么呢,和前边那个一直为0的原因一样,我们做删除操作等于用空来替换一个字符,所以替换的长度为空的长度,也就是0;

这个原理理解了的话,对于void onTextChanged(CharSequence s, int start, int before, int count); 和这个类似,官方文档如下:

void onTextChanged (CharSequence s, int start, int before, int count)This method is called to notify you that, within s, the count characters beginning at start have just replaced old text that had length before. It is an error to attempt to make changes to s from this callback. 意思大概是:这个方法是用来告诉你,在字符串s里,光标start开始处的count个字符刚刚把原来的before长度的字符替换。理解为通知你刚刚做,或正在做,主要与后边afterTextChanged()方法区分;

代码更改如下:

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {toast("s字符串为:"+s+",开始处:"+start+",替换体长度:"+count+",前替换体长度"+before);
}@Override
public void afterTextChanged(Editable s) {}

分别输入一个字符、二个字符、删除一个字符的截图:

这里写图片描述 这里写图片描述 这里写图片描述

结合第一个理解:这个方法在你刚刚操作后提示你:

输入一个字符:s替换后后执行此方法,所以可以读取到字符串“你”;开始处0:下标位,接下来不说了这个。替换体:也就是你输入的字符长度,“你”的长度为1;前替换体:0,你用“你”这个字符替换了长度为0的字符。
输入二个字符:替换体为你输入的“你好”的长度2;前替换体:0,同上。
删除一个字符:s字符为“你你”;替换体长度:0,好比你用长度为0的字符代替了“你”字;前替换体:1,“你”的长度。

对于 void afterTextChanged(Editable s):

官方解释:

This method is called to notify you that, somewhere within s, the text has been changed. It is legitimate to make further changes to s from this callback, but be careful not to get yourself into an infinite loop, because any changes you make will cause this method to be called again recursively. (You are not told where the change took place because other afterTextChanged() methods may already have made other changes and invalidated the offsets. But if you need to know here, you can use setSpan(Object, int, int, int) in onTextChanged(CharSequence, int, int, int) to mark your place and then look up from here where the span ended up.

意思是:这个方法告诉你,在字符串s内的某处,一些地方已经改变了,在这个方法里可以对s做一些改变,但是注意别让你陷入反复调用它的问题上,因为可能你做的任何改变会让他递归调用本身。(这个方法没有告诉你哪里改变了,或许其他afterTextChanged()可能已经改变它了并使这个改变失效,但是如果你确定知道他是否起作用了,你可以在onTextChanged()里调用setSpan(Object, int, int, int)方法去标记并在此验证是否在这结束了))

来吧我们来验证一下这个方法做了什么事:

修改代码:

            @Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}@Overridepublic void afterTextChanged(Editable s) {s.append("hello");toast(s.toString());}

假如我们输入“你”字,预测结果是不是:

你hello

然而结果是:

.......竟然ANR了。也就是程序无响应了 -_-

修改EditText的输入长度为15再试试:

android:maxLength="15"

运行截图:
这里写图片描述

达到最大长度才显示了一下,我们可以推测到,他竟然递归调用了。。。所以文档中说到谨慎一点,小心陷入递归自身的问题。所以这个方法是在你输入完后才调用的,你输入一个字符,然后以后是不是一直处于输入完成状态,所以他一直在调用这个方法。



总结
一般我们在onTextChanged();方法里做一些自己要做的事,比如监听输入的字符长度,或者应用在验证输入一个手机号就设置按钮可点击等等。
beforeTextChanged();在View改变之前执行,好比你输入了字符,系统先统计你输入的信息,在这里可以提前获取你的动机。
onTextChanged();在View改变之后短时间内执行,也就是区别afterTextChanged();的一直执行状态,他只调用一次。我们做自己的操作一般在这里;
afterTextChanged();在你输入完成后执行,我们输入完后处于完成状态,他就监测到完成了就不断的执行,因为我们不操作,是不是一直处于完成状态?所以就处于死循环了。切记在此做操作。

好了,关于监控EditText就说到这里,看了后会不会更清晰怎么用了呢?


http://chatgpt.dhexx.cn/article/93ISHAZP.shtml

相关文章

Android TextWatcher三个回调详解,监听EditText的输入

TextWatcher是一个监听字符变化的类。当我们调用EditText的addTextChangedListener(TextWatcher)方法之后&#xff0c;就可以监听EditText的输入了。 在new出一个TextWatcher之后&#xff0c;我们需要实现三个抽象方法&#xff1a; beforeTextChangedonTextChangedafterTextCh…

android TextWatcher 学习

1.简介 主要用来监听用户输入&#xff0c;然后剪裁输入。 比如输入框只能输入8个字节的内容&#xff0c;就可以用TextWatcher来实现。 public interface TextWatcher extends NoCopySpan {/*** This method is called to notify you that, within <code>s</code>…

android的TextView的TextWatcher使用

TextWatcher是一个文本变化监听接口&#xff0c;定义了三个接口&#xff0c;分别是beforeTextChanged,onTextChanged,afterTextCahnged. TextWatcher通常与TextView结合使用&#xff0c;以便在文本变化的不同时机做响应的处理。TextWatcher中三个回调接口都是使用了InputFilter…

Android 文本监听接口TextWatcher详解

TextWatcher是一个用来监听文本变化的接口&#xff0c;使用该接口可以很方便的对可显示文本控件和可编辑文本控件中的文字进行监听和修改 TextWatcher接口中定义了三个方法&#xff1a; public void beforeTextChanged(CharSequence s, int start, int count, int after) {} 该…

makefile中的两个函数(wildcard和patsubst)

(1) wildcard函数 作用是查找指定目录下指定类型的文件&#xff0c;并最终返回一个环境变量&#xff0c;需要用$取值赋值给另一个环境变量&#xff01;该函数只有一个参数&#xff0c;如取出当前目录下的所有.c文件&#xff0c;并赋值给allc普通变量&#xff1a; allc$(wildc…

linux_makefile文件编写,基本规则、工作原理、模式规则,wildcard函数、patsubst函数

接上一篇&#xff1a;linux_GDB调试学习(调试运行、多文件设置断点)_C/C程序调试 本次来分享linux下的makefile文件的编写&#xff0c;开始上菜&#xff1a; 目录 1.makefile文件的命名规则2.用途3.基本规则3.1.用例一4.工作原理4.1.用例二 5.makefile的执行5.1.用例三 6.make…

makefile wildcard patsubst使用小结

这是当前的目录&#xff1a; objs main.o g_a.o g_b. target : $(objs)gcc -o target $(objs) main.o : g_a.h g_b.hgcc -c main.c g_a.o : g_a.hgcc -c g_a.c g_b.o : g_b.hgcc -c g_b.c.PHONY : cleansrc $(wildcard *.c) obj $(patsubst %.c,%.o,$(src)) #obj $(patsu…

Makefile中patsubst函数使用方法

Makefile中patsubst函数使用方法 patsubst函数用于将文件模式进行替换。 一、作用 替换文件后缀。 二、格式 $(patsubst 原模式&#xff0c; 目标模式&#xff0c; 文件列表) 三、实例 图1 源文件结构 图2 patsubst实例

从零开始学习makefile(5)makefile中patsubst的作用

目录 介绍 text pattern与replacement 返回值 通配符% 示例1 例子2 介绍 patsubst是pattern substitute的缩写。其用法是&#xff1a; $(patsubst pattern,replacement,text) text text是将要被处理的字符串。首先&#xff0c;patsubst以空格为分隔符&#xff0c;将te…

list_for_each_entry和list_for_each_entry_safe

看内核代码都会发现&#xff0c;内核链表的操作常用的二个宏list_for_each_entry和list_for_each_entry_safe 循序渐进&#xff0c;先从最底层的函数container_of函数说起&#xff0c;其内核定义如下&#xff1a; 先看offsetof宏&#xff0c;根据优先级的顺序&#xff0c;最里面…

Map中的entry,entrySet,keySet的区别和用法

Map中的元素是以键值对的形式存在的&#xff0c;即key-value。key是唯一的不能重复&#xff0c;但value可以重复。 Map.keySet(): 这个方法返回的就是map集合中所有键Key的一个Set集合。如Map<Integer&#xff0c;String> 中put(1, “张三”)&#xff0c;put(2, “李四”…

Map.Entry与entrySet与entry,getKey()与entry.getValue()的用法

直接上代码 实体类 Data AllArgsConstructor NoArgsConstructor public class SinglePressureResultDTO {private Integer Times; private Integer SCU_number; private Boolean Intervention; private Long startTime_low; private Long low_time; private Long start…

Map集合的entrySet()方法

之前学习集合的时候要通过迭代器来迭代的时候最难得就是map集合得迭代&#xff0c;一直也不太明白&#xff0c;今天总算搞懂了 首先我们看什么容器才能迭代 根据API我们得知是对所有collection迭代的集合&#xff0c;那么已知的collection容器有哪些 我把常用的标出了&#xf…

keySet()和entrySet()

一、描述 keySet()和entrySet()&#xff0c;是Map集合中的两种取值方法。 与get(Object key)相比&#xff0c;get(Object key)只能返回到指定键所映射的值&#xff0c;不能一次全部取出。而keySet()和entrySet()可以。 Map集合中没有迭代器&#xff0c;Map集合取出键值的原理…

Java高级之HashMap中的entrySet()方法

基本使用 entrySet()方法得到HashMap中各个键值对映射关系的集合。 然后Map.Entry中包含了getKey()和getValue()方法获取键和值。 示例&#xff1a; public class Demo {public static void main(String[] args) {Map<String, String> map new HashMap<>();ma…

KeySet和EntrySet区别

场景&#xff1a; keySet()和entrySet()&#xff0c;是Map集合中的两种取值方法。 与get(Object key)相比&#xff0c;get(Object key)只能返回到指定键所映射的值&#xff0c;不能一次全部取出。而keySet()和entrySet()可以。 Map集合中没有迭代器&#xff0c;Map集合取出键…

Map中entrySet()方法使用

public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。 就是返回一个集合&#xff0c;集合里存放的是对象&#xff0c;创建对象的类有两个属性&#xff0c;分别是 键和值 也即键值对。 其中Entry是属于Map的静态内部类&#x…

glPushMatrix 和glPopMatrix图解 ----求别笑

猜想&#xff1a; openGL在绘制场景时的一般用法是&#xff1a; 首先在函数的开始处用glLoadIdentity()设置当前的矩阵为单位矩阵。 然后在函数中用glPushMatrix()和glPopMatrix()函数进行操作&#xff1a; 根据实践判断&#xff1a; 即这两者是分开的&#xff0c;并不是当前…

OpenGL的glPushMatrix和glPopMatrix矩阵栈顶操作函数详解

OpenGL中图形绘制后&#xff0c;往往需要一系列的变换来达到用户的目的&#xff0c;而这种变换实现的原理是又通过矩阵进行操作的。opengl中的变换一般包括视图变换、模型变换、投影变换等&#xff0c;在每次变换后&#xff0c;opengl将会呈现一种新的状态&#xff08;这也就是…

为什么调用glPushMatrix()和glPopMatrix()

2019独角兽企业重金招聘Python工程师标准>>> 今天忽然感悟到为什么在进行变换之前要用glPushMatrix();这个函数&#xff0c;而在变换完毕后有用glPopMatrix()这两个函数了,赶紧记下来&#xff1a; 我们在变换坐标的时候&#xff0c;使用的是glTranslatef(),glRotaef…