使用SpannableString设置TextView部分可点击后,长按事件难以触发的问题
解决TextView添加ClickableSpan后长按事件失效的问题
问题原因
当给TextView设置LinkMovementMethod.getInstance()后,该MovementMethod会接管所有触摸事件的处理流程,包括长按事件的检测,导致TextView本身设置的OnLongClickListener无法触发。
解决方案
方案一:自定义MovementMethod继承LinkMovementMethod
重写onLongClick方法,手动触发TextView的长按事件:
class CustomLinkMovementMethod : LinkMovementMethod() { override fun onLongClick(widget: View, buffer: Spannable, event: MotionEvent): Boolean { (widget as? TextView)?.performLongClick() return super.onLongClick(widget, buffer, event) } companion object { private var instance: CustomLinkMovementMethod? = null fun getInstance(): CustomLinkMovementMethod { if (instance == null) { instance = CustomLinkMovementMethod() } return instance!! } } }
修改原方法中的MovementMethod设置:
// 替换原来的LinkMovementMethod.getInstance() textView.movementMethod = CustomLinkMovementMethod.getInstance()
方案二:通过OnTouchListener手动处理长按逻辑
给TextView设置触摸监听器,在触摸事件中检测长按时长,同时保留ClickableSpan的点击功能:
// 在调用makeTextLink方法后添加以下代码 var longPressStartTime: Long = 0 const val LONG_PRESS_THRESHOLD = 500 // 对应系统默认长按触发时间 textView.setOnTouchListener { v, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { longPressStartTime = System.currentTimeMillis() // 让LinkMovementMethod处理初始触摸事件,保证点击Span的功能正常 LinkMovementMethod.getInstance().onTouchEvent(v as TextView, v.text as Spannable, event) true } MotionEvent.ACTION_MOVE -> { val elapsed = System.currentTimeMillis() - longPressStartTime if (elapsed >= LONG_PRESS_THRESHOLD) { v.performLongClick() } true } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { val elapsed = System.currentTimeMillis() - longPressStartTime if (elapsed < LONG_PRESS_THRESHOLD) { // 短按事件交给LinkMovementMethod处理,触发Span的点击 LinkMovementMethod.getInstance().onTouchEvent(v as TextView, v.text as Spannable, event) } true } else -> false } }
注意事项
- 方案一中的自定义MovementMethod采用单例模式,和系统默认实现保持一致,避免重复创建实例。
- 两种方案都能保留ClickableSpan的点击功能,同时触发TextView的长按事件,可根据实际场景选择。
内容的提问来源于stack exchange,提问作者NDC00
相关产品推荐
相关产品推荐

