怎么利用Android仿微博正文链接交互效果
本篇内容主要讲解“怎么利用Android仿微博正文链接交互效果”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么利用Android仿微博正文链接交互效果”吧!
一、链接的匹配和显示交互
首先我们先分析一下链接的组成部分,可以肯定的是需要一个显示的标题,我们可能会对这个标题在UI表现上做些处理(常见的是一个链接的标志和设置不同的颜色)来提示和吸引用户的注意,另外还需要点击时跳转的链接,这条链接可以是内部也可以是外部(这就属于业务的需求)。关于链接的匹配方式可能会有不同的方案,我们这里选择了使用a标签的匹配方式,也就是接口会把链接的数据以a标签的形式给我们,客户端来进行匹配数据,下边我们简单的举一个例子,接口返回数据如下:
"你说的<a href="https://www.qq.com" rel="external nofollow" rel="external nofollow" >我是链接</a>11111<a href="https://www.baidu.com" rel="external nofollow" rel="external nofollow" >我也是链接</a>好开心啊,哈哈哈哈"
接下来我们对数据的处理:
suspend fun computeLenFilterLink(text: String, mContext: Context): SpannableStringBuilder = withContext(Dispatchers.Default) { var strings = SpannableStringBuilder(text) val pattern = "<a \s*href\s*=\s*(?:.*?)>(.*?)</a\s*>" val p = Pattern.compile(pattern) val matcher = p.matcher(strings) while (matcher.find()) { val str = matcher.group() val linkTitle = matcher.group(1) ?: "" //a标签链接正则匹配 val patternUrlString = "\s*(?i)href\s*=\s*("([^"]*")|'[^']*'|([^'">\s]+))" val patternUrl = Pattern.compile( patternUrlString, Pattern.CASE_INSENSITIVE ) //链接url val matcherUrL = patternUrl.matcher(strings) var linkUrl = "" while (matcherUrL.find()) { linkUrl = matcherUrL.group() linkUrl = linkUrl.replace("href\s*=\s*(['|"]*)".toRegex(), "") linkUrl = linkUrl.replace("['|"]".toRegex(), "") linkUrl = linkUrl.trim { it <= ' ' } break } //设置颜色 val sb = SpannableString("#$linkTitle") sb.setSpan( ForegroundColorSpan( ContextCompat.getColor(mContext, R.color.link_color) ), 0, sb.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) sb.setSpan( object : MyLinkClickSpan(mContext) { override fun onSpanClick(widget: View?) { //链接跳转 Toast.makeText(mContext, "点击链=$linkUrl", Toast.LENGTH_SHORT).show() } }, 0, sb.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) val start = strings.indexOf(str) strings.delete(start, start + str.length) //插入链接 strings.insert(start, sb) } return@withContext strings }
这里我们是用正则匹配的方式取出链接的标题和跳转链接,然后设置链接的标志和颜色替换原来的a标签插入正文数据中。接下来就是设置显示的数据:
val string = "你说的<a href="https://www.qq.com" rel="external nofollow" rel="external nofollow" >我是链接</a>11111<a href="https://www.baidu.com" rel="external nofollow" rel="external nofollow" >我也是链接</a>好开心啊,哈哈哈哈"lifecycleScope.launch { val contentString = LinkCheckHelper.computeLenFilterLink(string,this@MainActivity) tvLink.text = contentString}
二、链接的点击交互
参考微博的交互效果我们会发现当我们触摸链接内容时其背景会由透明变为链接文字的颜色,手指抬起时又置回透明。细心的你肯定发现我们在刚才链接数据的插入时设置了MyLinkClickSpan做了对链接点击的处理,那么背景颜色的改变就要在ClickSpan中的 updateDrawState(ds: TextPaint)方法中进行处理,代码如下:
class MyLinkClickSpan(private val context: Context) : ClickableSpan(), IPressedSpan { private var isPressed = false abstract fun onSpanClick(widget: View?) override fun onClick(widget: View) { if (ViewCompat.isAttachedToWindow(widget)) { onSpanClick(widget) } } override fun setPressed(pressed: Boolean) { isPressed = pressed } override fun updateDrawState(ds: TextPaint) { if (isPressed) { ds.bgColor = ContextCompat.getColor(context, R.color.link_bg_color) } else { ds.bgColor = ContextCompat.getColor(context, android.R.color.transparent) } ds.isUnderlineText = false }}
我们发现背景颜色的变化是需要对手指按下和抬起分别进行处理,所以这时不可避免的我们就要对触摸事件进行处理:
class LinkTextView(context: Context, attrs: AttributeSet?) : AppCompatTextView(context, attrs) { private var mPressedSpan: IPressedSpan? = null init { isFocusable = false isLongClickable = false// 有链接点击需求不设置则点击无效 movementMethod = MyLinkMovementMethod.instance highlightColor = Color.TRANSPARENT } override fun onTouchEvent(event: MotionEvent): Boolean { val text = text val spannable = Spannable.Factory.getInstance().newSpannable(text) if (event.action == MotionEvent.ACTION_DOWN) { //按下时记下clickSpan mPressedSpan = getPressedSpan(this, spannable, event) } return if (mPressedSpan != null) { //如果有clickSpan就走MyLinkMovementMethod的onTouchEvent MyLinkMovementMethod.instance.onTouchEvent(this, getText() as Spannable, event) } else { super.onTouchEvent(event) } } private fun getPressedSpan( textView: TextView, spannable: Spannable, event: MotionEvent ): IPressedSpan? { var mTouchSpan: IPressedSpan? = null var x = event.x.toInt() var y = event.y.toInt() x -= textView.totalPaddingLeft x += textView.scrollX y -= textView.totalPaddingTop y += textView.scrollY val layout = textView.layout val line = layout.getLineForVertical(y) try { var off = layout.getOffsetForHorizontal(line, x.toFloat()) if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) { // 实际上没点到任何内容 off = -1 } val linkSpans = spannable.getSpans(off, off, IPressedSpan::class.java) if (!linkSpans.isNullOrEmpty()) { mTouchSpan = linkSpans[0] } return mTouchSpan } catch (e: IndexOutOfBoundsException) { Log.d(this.toString(), "getPressedSpan", e) } return null }}
class MyLinkMovementMethod : LinkMovementMethod() { override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean { return (sHelper.onTouchEvent(widget, buffer, event)) } companion object { val instance: MyLinkMovementMethod get() { if (sInstance == null) { sInstance = MyLinkMovementMethod() } return sInstance as MyLinkMovementMethod } private var sInstance: MyLinkMovementMethod? = null private val sHelper = SpanClickHelper() }}
class SpanClickHelper { private var mPressedSpan: IPressedSpan? = null fun onTouchEvent( textView: TextView, spannable: Spannable, event: MotionEvent ): Boolean { return when (event.action) { MotionEvent.ACTION_DOWN -> { mPressedSpan = getPressedSpan(textView, spannable, event) if (mPressedSpan != null) { //手指按下 设置按下为true,修改对应的链接文字背景颜色 mPressedSpan!!.setPressed(true) //设置选中区域 Selection.setSelection( spannable, spannable.getSpanStart(mPressedSpan), spannable.getSpanEnd(mPressedSpan) ) } mPressedSpan != null } MotionEvent.ACTION_MOVE -> { val touchedSpan = getPressedSpan(textView, spannable, event) if (mPressedSpan != null && touchedSpan != mPressedSpan) { //手指移动时 设置按下为false,对应的链接文字背景颜色置回透明 mPressedSpan!!.setPressed(false) mPressedSpan = null //移除选中区域 Selection.removeSelection(spannable) } mPressedSpan != null } MotionEvent.ACTION_UP -> { var touchSpanHint = false if (mPressedSpan != null) { touchSpanHint = true //手指抬起时 设置按下为false,对应的链接文字背景颜色置回透明 mPressedSpan!!.setPressed(false) //传递点击事件回调 mPressedSpan!!.onClick(textView) } mPressedSpan = null Selection.removeSelection(spannable) touchSpanHint } else -> { if (mPressedSpan != null) { //其它收拾 都设置按下为false,对应的链接文字背景颜色置回透明 mPressedSpan!!.setPressed(false) } //移除选中区域 Selection.removeSelection(spannable) false } } } private fun getPressedSpan( textView: TextView, spannable: Spannable, event: MotionEvent ): IPressedSpan? { var x = event.x.toInt() var y = event.y.toInt() x -= textView.totalPaddingLeft y -= textView.totalPaddingTop x += textView.scrollX y += textView.scrollY val layout = textView.layout val line = layout.getLineForVertical(y) try { var off = layout.getOffsetForHorizontal(line, x.toFloat()) if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) { // 实际上没点到任何内容 off = -1 } val link = spannable.getSpans( off, off, IPressedSpan::class.java ) var touchedSpan: IPressedSpan? = null if (link.isNotEmpty()) { touchedSpan = link[0] } return touchedSpan } catch (e: IndexOutOfBoundsException) { Log.d(this.toString(), "getPressedSpan", e) } return null }}
代码比较简单,就是对点击区域判断是否为链接,然后根据手势的操作分别设置给ClickSpan是否按下,来改变链接的背景颜色。
另外需要注意一点的是必须要调用下边的代码:
movementMethod = MyLinkMovementMethod.instance
此方法设置链接的点击。 另外,不同的机型上系统会有一个链接的高亮颜色,我们需要调用
highlightColor = Color.TRANSPARENT
来取消掉。
这样,链接的显示和点击交互就完成了。 具体效果:
到此,相信大家对“怎么利用Android仿微博正文链接交互效果”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341