android TextWatcher 学习
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 学习相关推荐
- java/android 设计模式学习笔记(1)--- 单例模式
前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...
- Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition
Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition Property animation系统还提供了对ViewGroup中的View改变 ...
- android Fragment 学习资料推荐
为什么80%的码农都做不了架构师?>>> android Fragment 学习资料推荐:android大神 郭霖 http://blog.csdn.net/guolin_ ...
- android service 学习(上)
转载自:http://www.cnblogs.com/allin/archive/2010/05/15/1736458.html Service是android 系统中的一种组件,它跟Activity ...
- android service 学习(下)
android service 学习(下) 通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service.在android ...
- Android:学习AIDL,这一篇文章就够了(下)
前言 上一篇博文介绍了关于AIDL是什么,为什么我们需要AIDL,AIDL的语法以及如何使用AIDL等方面的知识,这一篇博文将顺着上一篇的思路往下走,接着介绍关于AIDL的一些更加深入的知识.强烈建议 ...
- Android Binder 学习笔记
前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...
- Android画图学习总结(四)——Animation(中)
在Android画图学习总结(四)--Animation(上)中详细介绍了Tween Animation的定义.使用,由于篇幅有限,很多中重要的方面没有说明,这篇文章一方面做个完整的总结说明,另外一方 ...
- android培训内容明细,记录Android开发学习
记录Android开发学习 Menu菜单学习 1.掌握Android中菜单的创建. 2.掌握Intent信使组件. 创建菜单Menu 我们模仿微信菜单栏学习,创建一个于微信菜单栏相似的菜单 那么我们应 ...
- Android FrameWork学习(二)Android系统源码调试
点击打开链接 通过上一篇 Android FrameWork学习(一)Android 7.0系统源码下载\编译 我们了解了如何进行系统源码的下载和编译工作. 为了更进一步地学习跟研究 Android ...
最新文章
- 典型云平台技术栈有哪些?
- javaScript通用数据类型校验
- (十三) 构建dubbo分布式平台-dubbo管控台安装
- [云炬ThinkPython阅读笔记]1.5 值和类型
- 数据验证和JSR303
- codechef LEMOVIE dp
- Hello Blog!
- 从 HTML 提取文本的 7 个工具
- PDF权限密码怎么解除
- rs232接口_串口、COM口、UART口,TTL、RS-232、RS-485这么多接口,怎么区分
- 微信小程序SEO排名优化
- pg数据库的一些问题
- 计算机取证之Xplico ——合天网安实验室学习笔记
- 贪心算法之田忌赛马问题
- Oracle 对比两张表的数据是否一致
- 计算机or笔记本,笔记本or台式机 这几款戴尔主机性能上没得挑
- PyTorch读取自己的本地图片数据集训练自编码器
- c#winform使用EntityFramework导入数据库
- Java回炉之语言基础
- 项目经理成长的五个阶段
热门文章
- 移远NB-IOT BC28 模组 与自建的UDP服务器通信
- 大数据Hadoop详细介绍(v2016)
- matlab画圆(及其他常用图形)
- 用 Python 绘制了若干张词云图,惊艳了所有人
- Unity基础知识学习四,UI框架设计
- TLPI UNIX linux系统编程手册源代码运行
- 斐讯K1、K2、K2P 刷机、刷入Breed@重庆网吧电竞酒店维护小哥
- ARX二次开发 遍历删除所有的约束
- php 公众号采集器,别跑,教你微信公众号文章采集! - 八爪鱼采集器
- 华为交换机关闭网口_关闭端口的命令 怎么开启华为交换机关闭端口,命令谁知道啊。...