Android 仿微博正文链接交互 - 掘金

文章推薦指數: 80 %
投票人數:10人

社区内容经常会有插入链接的需要,这时就产生了对链接的UI和点击交互的需求,我们在微博中也经常会在列表页面和详情页面看到。

Android仿微博正文链接交互 笑慢 2021年11月28日21:09 ·  阅读1325 关注社区内容经常会有插入链接的需要,这时就产生了对链接的UI和点击交互的需求,我们在微博中也经常会在列表页面和详情页面看到。

下边我们就此功能分析一下具体实现。

一、链接的匹配和显示交互 首先我们先分析一下链接的组成部分,可以肯定的是需要一个显示的标题,我们可能会对这个标题在UI表现上做些处理(常见的是一个链接的标志和设置不同的颜色)来提示和吸引用户的注意,另外还需要点击时跳转的链接,这条链接可以是内部也可以是外部(这就属于业务的需求)。

关于链接的匹配方式可能会有不同的方案,我们这里选择了使用a标签的匹配方式,也就是接口会把链接的数据以a标签的形式给我们,客户端来进行匹配数据,下边我们简单的举一个例子,接口返回数据如下: "你说的我是链接11111我也是链接好开心啊,哈哈哈哈" 复制代码 接下来我们对数据的处理: /** *匹配a标签直接插入链接 */ suspendfuncomputeLenFilterLink(text:String,mContext:Context):SpannableStringBuilder= withContext(Dispatchers.Default){ varstrings=SpannableStringBuilder(text) valpattern="(.*?)" valp=Pattern.compile(pattern) valmatcher=p.matcher(strings) while(matcher.find()){ valstr=matcher.group() vallinkTitle=matcher.group(1)?:"" //a标签链接正则匹配 valpatternUrlString= "\s*(?i)href\s*=\s*("([^"]*")|'[^']*'|([^'">\s]+))" valpatternUrl=Pattern.compile( patternUrlString, Pattern.CASE_INSENSITIVE ) //链接url valmatcherUrL=patternUrl.matcher(strings) varlinkUrl="" while(matcherUrL.find()){ linkUrl=matcherUrL.group() linkUrl=linkUrl.replace("href\s*=\s*(['|"]*)".toRegex(),"") linkUrl=linkUrl.replace("['|"]".toRegex(),"") linkUrl=linkUrl.trim{it<=''} break } //设置颜色 valsb=SpannableString("#$linkTitle") sb.setSpan( ForegroundColorSpan( ContextCompat.getColor(mContext,R.color.link_color) ),0, sb.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE ) sb.setSpan( object:MyLinkClickSpan(mContext){ overridefunonSpanClick(widget:View?){ //链接跳转 Toast.makeText(mContext,"点击链=$linkUrl",Toast.LENGTH_SHORT).show() } }, 0,sb.length,Spanned.SPAN_INCLUSIVE_INCLUSIVE ) valstart=strings.indexOf(str) strings.delete(start,start+str.length) //插入链接 strings.insert(start,sb) } return@withContextstrings } 复制代码 这里我们是用正则匹配的方式取出链接的标题和跳转链接,然后设置链接的标志和颜色替换原来的a标签插入正文数据中。

接下来就是设置显示的数据: valstring="你说的我是链接11111我也是链接好开心啊,哈哈哈哈" lifecycleScope.launch{ valcontentString=LinkCheckHelper.computeLenFilterLink(string,this@MainActivity) tvLink.text=contentString } 复制代码 二、链接的点击交互 参考微博的交互效果我们会发现当我们触摸链接内容时其背景会由透明变为链接文字的颜色,手指抬起时又置回透明。

细心的你肯定发现我们在刚才链接数据的插入时设置了MyLinkClickSpan做了对链接点击的处理,那么背景颜色的改变就要在ClickSpan中的 updateDrawState(ds:TextPaint)方法中进行处理,代码如下: classMyLinkClickSpan(privatevalcontext:Context): ClickableSpan(),IPressedSpan{ privatevarisPressed=false abstractfunonSpanClick(widget:View?) overridefunonClick(widget:View){ if(ViewCompat.isAttachedToWindow(widget)){ onSpanClick(widget) } } overridefunsetPressed(pressed:Boolean){ isPressed=pressed } overridefunupdateDrawState(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 } } 复制代码 我们发现背景颜色的变化是需要对手指按下和抬起分别进行处理,所以这时不可避免的我们就要对触摸事件进行处理: classLinkTextView(context:Context,attrs:AttributeSet?): AppCompatTextView(context,attrs){ privatevarmPressedSpan:IPressedSpan?=null init{ isFocusable=false isLongClickable=false //有链接点击需求不设置则点击无效 movementMethod=MyLinkMovementMethod.instance highlightColor=Color.TRANSPARENT } overridefunonTouchEvent(event:MotionEvent):Boolean{ valtext=text valspannable=Spannable.Factory.getInstance().newSpannable(text) if(event.action==MotionEvent.ACTION_DOWN){ //按下时记下clickSpan mPressedSpan=getPressedSpan(this,spannable,event) } returnif(mPressedSpan!=null){ //如果有clickSpan就走MyLinkMovementMethod的onTouchEvent MyLinkMovementMethod.instance.onTouchEvent(this,getText()asSpannable,event) }else{ super.onTouchEvent(event) } } privatefungetPressedSpan( textView:TextView,spannable:Spannable, event:MotionEvent ):IPressedSpan?{ varmTouchSpan:IPressedSpan?=null varx=event.x.toInt() vary=event.y.toInt() x-=textView.totalPaddingLeft x+=textView.scrollX y-=textView.totalPaddingTop y+=textView.scrollY vallayout=textView.layout valline=layout.getLineForVertical(y) try{ varoff=layout.getOffsetForHorizontal(line,x.toFloat()) if(xlayout.getLineRight(line)){ //实际上没点到任何内容 off=-1 } vallinkSpans= spannable.getSpans(off,off,IPressedSpan::class.java) if(!linkSpans.isNullOrEmpty()){ mTouchSpan=linkSpans[0] } returnmTouchSpan }catch(e:IndexOutOfBoundsException){ Log.d(this.toString(),"getPressedSpan",e) } returnnull } } 复制代码 classMyLinkMovementMethod:LinkMovementMethod(){ overridefunonTouchEvent(widget:TextView,buffer:Spannable,event:MotionEvent):Boolean{ return(sHelper.onTouchEvent(widget,buffer,event)) } companionobject{ valinstance:MyLinkMovementMethod get(){ if(sInstance==null){ sInstance=MyLinkMovementMethod() } returnsInstanceasMyLinkMovementMethod } privatevarsInstance:MyLinkMovementMethod?=null privatevalsHelper=SpanClickHelper() } } 复制代码 classSpanClickHelper{ privatevarmPressedSpan:IPressedSpan?=null funonTouchEvent( textView:TextView, spannable:Spannable, event:MotionEvent ):Boolean{ returnwhen(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->{ valtouchedSpan=getPressedSpan(textView,spannable,event) if(mPressedSpan!=null&&touchedSpan!=mPressedSpan){ //手指移动时设置按下为false,对应的链接文字背景颜色置回透明 mPressedSpan!!.setPressed(false) mPressedSpan=null //移除选中区域 Selection.removeSelection(spannable) } mPressedSpan!=null } MotionEvent.ACTION_UP->{ vartouchSpanHint=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 } } } /** *判断手指是否点击在链接上 */ privatefungetPressedSpan( textView:TextView, spannable:Spannable, event:MotionEvent ):IPressedSpan?{ varx=event.x.toInt() vary=event.y.toInt() x-=textView.totalPaddingLeft y-=textView.totalPaddingTop x+=textView.scrollX y+=textView.scrollY vallayout=textView.layout valline=layout.getLineForVertical(y) try{ varoff=layout.getOffsetForHorizontal(line,x.toFloat()) if(xlayout.getLineRight(line)){ //实际上没点到任何内容 off=-1 } vallink=spannable.getSpans( off,off, IPressedSpan::class.java ) vartouchedSpan:IPressedSpan?=null if(link.isNotEmpty()){ touchedSpan=link[0] } returntouchedSpan }catch(e:IndexOutOfBoundsException){ Log.d(this.toString(),"getPressedSpan",e) } returnnull } } 复制代码 代码比较简单,就是对点击区域判断是否为链接,然后根据手势的操作分别设置给ClickSpan是否按下,来改变链接的背景颜色。

另外需要注意一点的是必须要调用下边的代码: movementMethod=MyLinkMovementMethod.instance 复制代码 此方法设置链接的点击。

另外,不同的机型上系统会有一个链接的高亮颜色,我们需要调用 highlightColor=Color.TRANSPARENT 复制代码 来取消掉。

这样,链接的显示和点击交互就完成了。

具体效果: 点击获取源码分类:Android标签:Android交互设计 安装掘金浏览器插件 多内容聚合浏览、多引擎快捷搜索、多工具便捷提效、多模式随心畅享,你想要的,这里都有!前往安装 关注 私信 获得点赞  149 文章被阅读  26,186下载稀土掘金APP一个帮助开发者成长的社区目录收藏成功! 已添加到「」, 点击更改微信微信扫码分享新浪微博QQ 沉浸阅读



請為這篇文章評分?