富文本开发那些事:如何一次性删除/跳过一段特殊文本,比如@xxx?

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

写过富文本开发的童鞋,应该都会面临这样一个情况,在对EditText进行编辑时,@了一个用户,界面上就会显示"@xxx",然后又突然想把这个"@xxx"删除掉,正常的行为应该是用户只点击一次虚拟键盘的删除按键,整条"@xxx"都应该被删除掉,而不是用户需要点击4次删除按键才能将"@xxx"给删除,或者是当光标滑动到"@xxx"特殊文本内部时,能直接跳转到该文本终点,本篇文章就是讲述如何实现这种效果 。

ClickableSpan标识”@xxx“

一般我们@某个用户并在界面上显示时,会将”@xxx“这段@文本颜色高亮,并支持点击跳转到该用户的个人主页。

下面我们借助ClickableSpan实现下:

val clickableSpan = object: ClickableSpan() {
            override fun onClick(widget: View) {
               //跳转到用户个人主页
            }
​
            override fun updateDrawState(ds: TextPaint) {
                ds.color = ds.linkColor
                ds.isUnderlineText = true
            }
        }
复制代码

其中:

  • 重写onClick()方法实现点击跳转到用户个人主页的逻辑
  • 重写updateDrawState()方法,调用ds.color = xxx设置”@xxx“文本显示颜色,一般默认设置ds.isUnderlineText = false,使得”@xxx“文本不显示下划线

选中@xxx再删除

首先我们要明确,当用户点击虚拟键盘删除按键时,要从EditText具体删除哪段文本是由Selection选中范围决定的,即删除的起为Selection.getSelectionStart()对应下标,终点到Selection.getSelectionEnd()对应下标。

所以,我们只需要在用户点击删除按键之后,手动调用EditTextsetSelection()方法指定选中范围即可,在这个场景中,我们只需要获取到”@xxx“在EditText中的起点和终点的位置即可,接下来我们一步步实现这个效果。

  • 监听用户点击虚拟键盘的删除按钮

    
    et.setOnKeyListener { v, keyCode, event ->
                if (keyCode == KeyEvent.KEYCODE_DEL && event.action == KeyEvent.ACTION_DOWN) {
                    //监听键盘的点击删除按钮事件
                }
    ​
                return@setOnKeyListener false
            }
    复制代码

    直接给EditText设置OnKeyListener监听,并且点击的标识为KeyEvent.KEYCODE_DEL就代表用户点击了删除按钮,请注意这个地方我们还判断了event.action == KeyEvent.ACTION_DOWN,也就是说当用户点击了删除按钮并不管有没有移开,就直接执行删除,有利于用户的交互。

  • 获取”@xxx“在EditText中的起始下标

    前面有讲,”@xxx“这段文本会被ClickableSpan标识,获取”@xxx“在文本的起始位置也就等价于获取ClickableSpan在文本中渲染的起始位置,系统已经给我们提供好了便利的API:

    
                //1.
                if (et.selectionEnd != et.selectionStart) {
                    return@setOnKeyListener false
                }
                //2.
                val spans = et.text.getSpans(0, et.text.length, ClickableSpan::class.java)
                spans.find {
                    //3.
                    et.text.getSpanEnd(it) == et.selectionEnd
                }?.let {
                    //如果存在满足上诉条件的ClickableSpan,则获取ClickableSpan的起始位置并直接选中该ClickableSpan
                    val start = et.text.getSpanStart(it)
                    val end = et.text.getSpanEnd(it)
                    et.setSelection(start, end)
                }
    ​
    复制代码
    • 首先我们要判断当前文本选中的起始点是否相同,不相同代表当前已经指定的选中范围,直接跳过下面才做即可
    • 借助getSpans获取EditText文本中所有的ClickableSpan类型数组,请注意,这个方法返回的Span的顺序是无序的
    • 然后筛选ClickableSpan数组中ClickableSpan标识的终点等于当前光标位置et.selectionEnd的元素
    • 如果找到符合上述条件的ClickableSpan,直接借助getSpanStartgetSpanEnd方法获取到该Span的起始点,这样也就代表着获取了”@xxx“在EditText中的起始点,然后调用setSelection方法进行选中即可

跳过一段特殊文本

  1. 首先还是要借助于EditTextsetSpan方法给这段特殊文本直接标识;
  2. 我们可以通过TextView的方法onSelectionChanged()监听光标所在的位置;
  3. 借助于这个效果,以及上面讲的相关知识,判断当前光标是否处在这个特殊文本起始范围内;
  4. 存在,我们就将光标的位置重新定位到该特殊文本的结束位置,这样就实现了一段特殊文本的跳过

这里就不再给大家实操了,按照上面所讲的思路,也是很容易实现的,就不再过多赘述了。

总结

经过上面的几步操作,我们就可以实现文章一开头说的效果:点击一次删除按钮,就能直接将光标之前的”@xxx“完整删除;或者跳过一段特殊文本。核心就是要给这段特殊文本通过setSpan方法增加一个标识才行。

猜你喜欢

转载自juejin.im/post/7101691081892298765
今日推荐