android TextWatcher 学习

android TextWatcher 学习androidTextW

1.简介

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

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

public interface TextWatcher extends NoCopySpan { / * 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. */ public 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 * have just replaced old text that had length before. * It is an error to attempt to make changes to s from * this callback. */ public void onTextChanged(CharSequence s, int start, int before, int count); / * 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 {@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 
  
    (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 = true private val what = "luo" private val editable = editText.editableText 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( MainActivity.TAG, "onTextChanged: s = $s, start = $start, before = $before, count = $count" ) s?.let { suitable = s.length <= limit if (!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、after editText.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] = start change[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、after editText.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().size Log.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.避免无限递归

代码icon-default.png?t=M4ADhttps://gitee.com/luoccxyz/text-watcher-test

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/226068.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 上午7:55
下一篇 2026年3月17日 上午7:55


相关推荐

  • leetcode-88合并两个有序数组(双指针)

    leetcode-88合并两个有序数组(双指针)题目链接给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。示例 1:输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3输出:[1,2,2,3,5,6]示例 2:输入:nums1 = [1]

    2022年8月8日
    6
  • windows内核编程_linux内核编程

    windows内核编程_linux内核编程什么是Windows内核编程

    2022年10月8日
    4
  • php 邮箱正则_正则表达式判断邮箱格式

    php 邮箱正则_正则表达式判断邮箱格式PHP邮箱验证正则表达式:preg_match(“/^[0-9a-zA-Z]+@(([0-9a-zA-Z]+)[.])+[a-z]{2,4}$/i”,$email);如果需要更加完善、严格的验证,修改这个正则表达式即可。PHP邮箱验证正则表达式新手实例:functionisEmail($email){if(preg_match(“/^[0-9a-zA-Z]+@(([0-9a-zA-Z]+)…

    2026年3月7日
    7
  • html div自动换行代码,css自动换行_css

    html div自动换行代码,css自动换行_css自动换行 word break break all 和 word wrap break wordword break break all 和 word wrap break word 都是能使其容器如 DIV 的内容自动换行 它们的区别就在于 1 word break break all 例如 div 宽 200px 它的内容就会到 200px 自动换行 如果该行末端有个英文单词很长 congratulati 等 它

    2026年3月18日
    2
  • Java之路:this关键字的用法

    Java之路:this关键字的用法(1)普通方法中,this总是指向调用该方法的对象。(2)构造方法中,this总是指向正要初始化的对象。(3)this可以让类中的一个方法,访问该类的另一个方法或属性。(4)可以使用this关键字调用重载构造方法。避免相同的初始化代码,只能在构造方法中用,并且必须位于构造方法的第一句。(5)this不能用于static方法。用this调用构造方法packagethis_const…

    2022年6月15日
    42
  • 八款常见的Android游戏引擎

    八款常见的Android游戏引擎1、AngleAngle是一款专为Android平台设计的,敏捷且适合快速开发的2D游戏引擎,基于OpenGLES技术开发。该引擎全部用Java代码编写,并且可以根据自己的需要替换里面的实现,缺陷在于文档不足,而且下载的代码中仅仅包含有少量的示例教程。最低

    2026年1月22日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号