1.简介

主要用来监听用户输入,然后剪裁输入。

比如输入框只能输入8个字节的内容,就可以用TextWatcher来实现。

public interface TextWatcher extends NoCopySpan {/*** This method is called to notify you that, within <code>s</code>,* the <code>count</code> characters beginning at <code>start</code>* are about to be replaced by new text with length <code>after</code>.* It is an error to attempt to make changes to <code>s</code> from* this callback.*/public void beforeTextChanged(CharSequence s, int start,int count, int after);/*** This method is called to notify you that, within <code>s</code>,* the <code>count</code> characters beginning at <code>start</code>* have just replaced old text that had length <code>before</code>.* It is an error to attempt to make changes to <code>s</code> from* this callback.*/public void onTextChanged(CharSequence s, int start, int before, int count);/*** This method is called to notify you that, somewhere within* <code>s</code>, the text has been changed.* It is legitimate to make further changes to <code>s</code> 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 {@link Spannable#setSpan} in {@link #onTextChanged}* to mark your place and then look up from here where the span* ended up.*/public void afterTextChanged(Editable s);
}

想要了解方法的参数什么意思,把注释看一下,然后把参数全部打印一遍就行。

这三个方法很简单,值得注意的地方:

每当输入一次,就会调用一次 before-on-after。每当调用setText("xxx")也会如此,那么在on方法中调用setText("xxx")就会形成递归,然后就可能死循环。

但毕竟监听这个就是要修改内容的,根据源码注释,可以在after中修改字符。

2.使用

val input1 = findViewById<EditText>(R.id.input1)
input1.addTextChangedListener(Watcher1(input1, 8, TAG))

先简单尝试,限制8个数字或字母

class Watcher1(private val editText: EditText, private val limit: Int, private val TAG: String): TextWatcher {private var suitable = trueprivate val what = "luo"private val editable = editText.editableTextoverride fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {Log.d(TAG, "beforeTextChanged")}override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {Log.d(MainActivity.TAG,"onTextChanged: s = $s, start = $start, before = $before, count = $count")s?.let {suitable = s.length <= limitif (!suitable) {editable.setSpan(what, start, start + count, 1)}}}override fun afterTextChanged(s: Editable?) {Log.d(TAG, "afterTextChanged")if (!suitable) {s?.let {// 注意substring的参数范围Log.d(TAG, "afterTextChanged: ${editable.getSpanStart(what)}")val start = s.substring(0, editable.getSpanStart(what))Log.d(TAG, "afterTextChanged: start =  $start")val end = s.substring(editable.getSpanEnd(what), s.length)Log.d(TAG, "afterTextChanged: end = $end")val text = start + end// 会继续调用before、on、aftereditText.setText(text)editText.setSelection(editable.getSpanStart(what))}}}
}

思路:在on中,字符串是已经被修改了的,所以在on中判断,如果字符串不符合要求,就记录这次的输入详情,然后在after中截取字符串。

值得注意的是,如果你使用了after回调的参数 "s",也许会有bug,因为after中调用setText(),就会形成递归:因为s是第一次的,还未修改,调用setText()修改后会有第二次after,第二次的参数才是真正想要的。所以最好是不用这个参数,而是使用editText的get方法。

混合输入时,限制长度

由于数字、汉字、表情的byte各不相同。如果我们想要保证输入的byte总数不超过定值,那就需要监听输入,然后剪裁。

class Watcher2(private val editText: EditText, private val limit: Int, private val TAG: String) :TextWatcher {private val change = IntArray(2)override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {Log.d(TAG, "beforeTextChanged")}override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {Log.d(TAG, "onTextChanged s = $s, start = $start, before = $before, count = $count")if (!suitable(s.toString())) {change[0] = startchange[1] = start + count}}override fun afterTextChanged(s: Editable?) {Log.d(TAG, "afterTextChanged")if (!suitable(s.toString())) {s?.let {// 注意substring的参数范围val start = s.substring(0, change[0])Log.d(TAG, "afterTextChanged: start =  $start")val end = s.substring(change[1], s.length)Log.d(TAG, "afterTextChanged: end = $end")val text = start + end// 会继续调用before、on、aftereditText.setText(text)editText.setSelection(change[0])handler.removeCallbacks(runnable)handler.postDelayed(runnable, 100)}}Log.d(TAG, "afterTextChanged: s = $s")}/*** Determine whether the current input is legal*/private fun suitable(str: String): Boolean {val size = str.toByteArray().sizeLog.d(TAG, "suitable: size = $size")return size <= limit}private val handler = object : Handler(Looper.getMainLooper()) {}private val runnable =Runnable {Toast.makeText(editText.context, "限制 $limit byte!", Toast.LENGTH_SHORT).show() }
}

在第一个案例中,我使用setSpan来记录输入详情,在这个案例中有点不好使,主要原因是长度不同的。

在after中截取了字符串后记得把输入光标挪个位置。

3.总结

1.弄清楚方法里的参数

2.根据需求,在on中记录输入,在after中截取字符串

3.避免无限递归

代码https://gitee.com/luoccxyz/text-watcher-test

android TextWatcher 学习相关推荐

  1. java/android 设计模式学习笔记(1)--- 单例模式

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

  2. Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition

    Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition Property animation系统还提供了对ViewGroup中的View改变 ...

  3. android Fragment 学习资料推荐

    为什么80%的码农都做不了架构师?>>>    android   Fragment 学习资料推荐:android大神 郭霖 http://blog.csdn.net/guolin_ ...

  4. android service 学习(上)

    转载自:http://www.cnblogs.com/allin/archive/2010/05/15/1736458.html Service是android 系统中的一种组件,它跟Activity ...

  5. android service 学习(下)

    android service 学习(下) 通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service.在android ...

  6. Android:学习AIDL,这一篇文章就够了(下)

    前言 上一篇博文介绍了关于AIDL是什么,为什么我们需要AIDL,AIDL的语法以及如何使用AIDL等方面的知识,这一篇博文将顺着上一篇的思路往下走,接着介绍关于AIDL的一些更加深入的知识.强烈建议 ...

  7. Android Binder 学习笔记

    前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...

  8. Android画图学习总结(四)——Animation(中)

    在Android画图学习总结(四)--Animation(上)中详细介绍了Tween Animation的定义.使用,由于篇幅有限,很多中重要的方面没有说明,这篇文章一方面做个完整的总结说明,另外一方 ...

  9. android培训内容明细,记录Android开发学习

    记录Android开发学习 Menu菜单学习 1.掌握Android中菜单的创建. 2.掌握Intent信使组件. 创建菜单Menu 我们模仿微信菜单栏学习,创建一个于微信菜单栏相似的菜单 那么我们应 ...

  10. Android FrameWork学习(二)Android系统源码调试

    点击打开链接 通过上一篇 Android FrameWork学习(一)Android 7.0系统源码下载\编译 我们了解了如何进行系统源码的下载和编译工作. 为了更进一步地学习跟研究 Android ...

最新文章

  1. 典型云平台技术栈有哪些?
  2. javaScript通用数据类型校验
  3. (十三) 构建dubbo分布式平台-dubbo管控台安装
  4. [云炬ThinkPython阅读笔记]1.5 值和类型
  5. 数据验证和JSR303
  6. codechef LEMOVIE dp
  7. Hello Blog!
  8. 从 HTML 提取文本的 7 个工具
  9. PDF权限密码怎么解除
  10. rs232接口_串口、COM口、UART口,TTL、RS-232、RS-485这么多接口,怎么区分
  11. 微信小程序SEO排名优化
  12. pg数据库的一些问题
  13. 计算机取证之Xplico ——合天网安实验室学习笔记
  14. 贪心算法之田忌赛马问题
  15. Oracle 对比两张表的数据是否一致
  16. 计算机or笔记本,笔记本or台式机 这几款戴尔主机性能上没得挑
  17. PyTorch读取自己的本地图片数据集训练自编码器
  18. c#winform使用EntityFramework导入数据库
  19. Java回炉之语言基础
  20. 项目经理成长的五个阶段

热门文章

  1. 移远NB-IOT BC28 模组 与自建的UDP服务器通信
  2. 大数据Hadoop详细介绍(v2016)
  3. matlab画圆(及其他常用图形)
  4. 用 Python 绘制了若干张词云图,惊艳了所有人
  5. Unity基础知识学习四,UI框架设计
  6. TLPI UNIX linux系统编程手册源代码运行
  7. 斐讯K1、K2、K2P 刷机、刷入Breed@重庆网吧电竞酒店维护小哥
  8. ARX二次开发 遍历删除所有的约束
  9. php 公众号采集器,别跑,教你微信公众号文章采集! - 八爪鱼采集器
  10. 华为交换机关闭网口_关闭端口的命令 怎么开启华为交换机关闭端口,命令谁知道啊。...