TextView实现点击部分文字跳转,实现微信朋友圈评论Item的显示效果
大家都熟悉微信朋友圈或者是贴吧里的某一条评论,
比如: 小A回复小B:大吉大利,今晚吃鸡,哈哈哈。
点击小A和小B可以跳转到用户页面,点击整个Item就会响应其它事件,比如弹出键盘输入回复。
要实现这样的效果其实很简单,先自定义TextView,通过SpannableStringBuilder设置富文本格式,然后通过setText设置就可以了,看起来简单,但里面其实是有一些坑的,比如我实现了这种效果后,但发现点击Item其它地方的时候没有响应别的事件了。本篇文章就跟大家分享这个小知识点。先看效果:
创建Bean对象
创建评论对象和用户对象CommentBean和CommentUserSpan
public class CommentBean {/* 评论内容 */private String comment;/* 评论人 */private UserBean user;/* 回复人 */private UserBean replyUser;/* 省略了get和set方法 */......
}public class UserBean {private String userId;private String userName;/* 省略了get和set方法 */......
}
bean对象没什么可说的。
自定义TextView
public class CommentTextView extends TextView {public CommentTextView(Context context) {this(context, null);}public CommentTextView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public CommentTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setMovementMethod(CommentUserMovementMethod.getInstance());}public void setText(CommentBean comment) {SpannableStringBuilder stringBuilder = new SpannableStringBuilder();if (comment.getUser() != null) {String str = comment.getUser().getUserName();stringBuilder.append(str);CommentUserSpan span = new CommentUserSpan(getContext(), comment.getUser());stringBuilder.setSpan(span, 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);}if (comment.getReplyUser() != null) {stringBuilder.append("回复");int start = stringBuilder.toString().length();String str = comment.getReplyUser().getUserName();stringBuilder.append(str);CommentUserSpan span = new CommentUserSpan(getContext(), comment.getReplyUser());stringBuilder.setSpan(span, start, start + str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);stringBuilder.append(":");}stringBuilder.append(comment.getComment());setText(stringBuilder);}}
我们要想把CommentBean 中的两个用户对象转化成可点击的就需要借助ClickableSpan,通过SpannableStringBuilder 在特地位置setSpan就可以实现点击效果,ClickableSpan从名字上来看就知道是可点击的意思,上述代码中的CommentUserSpan就是自定义的继承自ClickableSpan用来实现点击事件的类。细心的朋友可能会发现构造里面有个setMovementMethod,没错,这个方法就是能够处理TextView中的触摸事件和点击事件的,有同学说不是有onTouchEvent方法吗?,对,但MovementMethod可以实现TextView中各种Span对象的处理,而且MovementMethod是onTouchEvent方法逻辑中的一部分,有兴趣的可以查看TextView的源码,我们来看看CommentUserSpan这个类:
public class CommentUserSpan extends ClickableSpan {private UserBean user;private Context context;private boolean isPressed;private int normalColor;private int pressedColor;public CommentUserSpan(Context context, UserBean commentUser) {super();this.user = commentUser;this.context = context;normalColor = Color.TRANSPARENT;pressedColor = context.getResources().getColor(R.color.colorPressed);}@Overridepublic void onClick(View widget) {Toast.makeText(context, "点击" + user.getUserName(), Toast.LENGTH_SHORT).show();}public void setPressed(boolean isPressed) {this.isPressed = isPressed;}@Overridepublic void updateDrawState(TextPaint ds) {ds.setColor(Color.BLUE);ds.bgColor = isPressed ? pressedColor : normalColor;}
}
只需重写其中的onClick方法和updateDrawState方法,onClick用来处理点击事件,updateDrawState用来处理我们想要的视觉效果,比如文字颜色,背景颜色等等,ds.bgColor可以实现用户点击后背景变暗的效果,效果跟selector一样。这个类很简单,我们来看看CommentUserMovementMethod类:
public class CommentUserMovementMethod extends BaseMovementMethod {@Overridepublic boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {int action = event.getAction();int x = (int) event.getX();int y = (int) event.getY();x -= widget.getTotalPaddingLeft();y -= widget.getTotalPaddingTop();x += widget.getScrollX();y += widget.getScrollY();Layout layout = widget.getLayout();int line = layout.getLineForVertical(y);int off = layout.getOffsetForHorizontal(line, x);CommentUserSpan[] link = buffer.getSpans(off, off, CommentUserSpan.class);CommentUserSpan span = null;if (link.length > 0) {span = link[0];}switch (action) {case MotionEvent.ACTION_DOWN:if (span != null) {span.setPressed(true);Selection.setSelection(buffer, buffer.getSpanStart(span), buffer.getSpanEnd(span));return true;} else {Selection.removeSelection(buffer);}break;case MotionEvent.ACTION_UP:if (span != null) {span.onClick(widget);span.setPressed(false);Selection.removeSelection(buffer);return true;}Selection.removeSelection(buffer);break;case MotionEvent.ACTION_MOVE:if (span != null) {span.setPressed(false);return true;} else {Selection.removeSelection(buffer);}break;default:if (span != null) {span.setPressed(false);}Selection.removeSelection(buffer);break;}return false;}public static MovementMethod getInstance() {if (sInstance == null) {sInstance = new CommentUserMovementMethod();}return sInstance;}private static CommentUserMovementMethod sInstance;
}
这个类看起来就稍显复杂,其实也就做了两件事,
1、检测点击区域有没有我们设定的CommentUserSpan,有的话就处理相应的逻辑。
2、根据事件类型,处理CommentUserSpan是否是按压状态,用来实现点击效果的。
Android为TextView实现了三种MovementMethod,分别是ArrowKeyMovementMethod、LinkMovementMethod和ScrollingMovementMethod,从名字上来看就能很容易知道LinkMovementMethod是用来处理超链接的、ScrollingMovementMethod是用来处理滚动的,ArrowKeyMovementMethod一眼看不出来,其实大家也都熟悉,就是长安Edittext会出现选择文本,然后弹出ContextMenu,剪切、复制、粘贴这些操作。
CommentUserMovementMethod 直接继承了基类BaseMovementMethod 。主要是重写其中的onTouchEvent方法,最关键的一行是Layout layout = widget.getLayout(); 然后结合event.getX()和event.getY()就可以定位到点击位置的文字,然后查找这个位置有没有我们要处理的CommentUserSpan,有的话就消费这个点击事件,没有的话就不消费事件,抛给父View处理。
到这里基本上就已经实现了效果,但是,有一个非常不好的体验。就是当我点击非用户区域文字的时候发现响应不了整个Item的点击事件了,也就是我想点击整个条目然后弹出键盘这个效果实现不了了,难道点击其它区域的事件也被CommentUserMovementMethod 消费了?于是debug一下,发现也没有消费啊,一时间没有思路了,但可以肯定的是,事件一定被TextView消费了,找了半天,终于发现了坑,还记得CommentTextView 中调了setMovementMethod方法吗?这是源码:
public final void setMovementMethod(MovementMethod movement) {if (mMovement != movement) {mMovement = movement;if (movement != null && !(mText instanceof Spannable)) {setText(mText);}fixFocusableAndClickableSettings();// SelectionModifierCursorController depends on textCanBeSelected, which depends on// mMovementif (mEditor != null) mEditor.prepareCursorControllers();}}
其中关键的一句fixFocusableAndClickableSettings(); 处理TextView的焦点和点击。
private void fixFocusableAndClickableSettings() {if (mMovement != null || (mEditor != null && mEditor.mKeyListener != null)) {setFocusable(true);setClickable(true);setLongClickable(true);} else {setFocusable(false);setClickable(false);setLongClickable(false);}}
进去一看,恍然大悟,了解事件分发机制的同学应该都知道,当View的Clickable和LongClickable为true时,onTouchEvent方法必然会返回true,坑就在这。知道了原因,问题自然迎刃而解。我们可以在调用setMovementMethod方法后再把Clickable和LongClickable设为false就行。
public CommentTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setMovementMethod(CommentUserMovementMethod.getInstance());setClickable(false);setLongClickable(false);}
好了,到这就完全实现微信朋友圈评论Item的效果了。
源码地址:
https://github.com/469412882/CommentTextApp
TextView实现点击部分文字跳转,实现微信朋友圈评论Item的显示效果相关推荐
- 文字 竖排居中_微信朋友圈文字如何设置居中居右?
每次发朋友圈,选好图片,输入构思已久的文字内容,点击发布! 静静的等待着...会有谁给我点赞呢?会有谁给我评论呢?然后,就没有然后了...很多刷存在感的小伙伴和做微商的小伙伴是不是都有这样的困惑?构思 ...
- android 微信朋友圈 全功能,Android仿微信朋友圈文字展开全文功能 Android自定义TextView仿微信朋友圈文字展开全文功能...
Android自定义TextView仿微信朋友圈文字信息,展开全文功能 代码及注释如下: 首先写一个xml文件 showmore.xml: android:orientation="vert ...
- H5静态页面跳转微信小程序;从外部浏览器,点击H5链接跳转打开微信小程序;以及在微信内直接点击H5链接打开微信小程序;
参考链接 需求:从外部浏览器,点击H5链接跳转打开微信小程序:以及在微信内直接点击H5链接打开微信小程序: 步骤1: 小程序开发需要使用云开发创建项目,使用云开发生成的项目会自带云函数文件夹: 步骤2 ...
- BUG集合-微信分享点击之后无法跳转到微信app应用进行分享
微信分享点击之后无法跳转到微信app应用进行分享 微信分享点击之后无法跳转到微信app应用进行分享的错误原因:提供的缩略图过大,所以无法跳转, 这个参数需要的图片不能过大 解决方案:1.换一张小的图片 ...
- android 微信朋友圈 全功能,Android自定义TextView仿微信朋友圈文字展开全文功能
Android自定义TextView仿微信朋友圈文字信息,展开全文功能 代码及注释如下: 首先写一个xml文件 showmore.xml: android:orientation="vert ...
- jsp文字上下居中显示_微信朋友圈又有骚技巧,一键设置居中签名,好友傻眼了...
今日推荐:微信朋友圈签名居中 适用:安卓.苹果 大家好我是小雷,又来给大家安利微信小技巧了,今天给大家分享如何让你的朋友圈签名居中显示.熟悉微信的朋友都知道,在微信设置了个性签名之后,往往会同步到朋友 ...
- js sdk 一键分享 微信_微信朋友圈分享自己拍的视频,一键开启这个设置,自带文字和音乐...
你分享到朋友圈视频,还是简单的随手一拍,然后分享吗?怎样让你的分享的视频看起来高端.大气.上档次呢?其实微信视频新增编辑功能.能剪辑,能加文字和音乐,还能添加表情包.让你的视频更好看. 1.拍摄阶段 ...
- android导出微信朋友圈怎么发文字,微信朋友圈怎么发纯文字?看完这篇文章,你就知道该怎么操作了...
经常使用微信聊天的朋友,一定会发现明明微信朋友圈只支持发视频或图片,然后配文字发送,为什么有些人的朋友圈可以发送纯文字呢?本期文章就教教大家如何使用微信"发纯文字"的朋友圈. 以安 ...
- 微信朋友圈怎么发文字?纯文字动态发布教程
微信朋友圈怎么发文字?除了日常的聊天之外,我们还可以通过微信的朋友圈功能发表动态,分享自己的心情.比较常见的朋友圈动态都是文字+图片的类型,发布的操作也比较简单,直接通过微信->发现->朋 ...
最新文章
- facerec = dlib.face_recognition_model_v1()面部识别器用法
- 他给女朋友做了个树莓派复古相机,算法代码可自己编写,成本不到700元
- JavaScript原型-进阶者指南
- python自动登录教程_python实现校园网自动登录的示例讲解
- SQLite添加列的限制
- 那些年Android黑科技③:干大事不择手段
- 时间处理总结(二)oracle
- 简单计算机面试题库及答案_计算机专业复试面试问题含答案
- php中$tpl= add_member_info ;什么意思,DEDECMS会员信息在个人模板info和index的调用问题...
- BZOJ - 2783 树
- 21次课(安装软件包的三种方法、rpm包介绍、rpm工具用法、yum工具用法、 yum搭建本地仓库)...
- 无线网络技术学习总结
- 开源PDF文件处理工具箱
- java oracle 乱码_oracle中的数据库乱码的原因与解决
- OPencv 灰度直方图、直方图规定化
- Spring开发指南_夏昕 问题总结
- 计算机的发展是小报,电脑小报作品展示评价与交流.ppt
- 如何编写外挂 制作外挂 外挂教程
- 浏览器-错误 未能加载 PDF 文档
- 矩阵在游戏开发中的应用
热门文章
- Kotlin项目实战之手机影音---项目介绍、项目启动
- xp linux 桌面快捷方式,清理WinXP系统桌面上的快捷方式图标(转)
- 数字化发展-数字化转型网
- js获取应用服务器时间,JavaScript获取服务器端时间的方法
- 贵州大学计算机上机复试题,复试全程:2011贵州大学计算机学院复试经历、笔试真题和上机...
- 航五路服务器docker启动
- Linuxc基础 八
- c++快速实现比较三个数的大小
- bowtie和bowtie2使用条件区别及用法
- python字符串类型的计算公式