内容摘要

该控件能够应用于内容资讯展示的功能模块中,如:腾讯和新浪微博的微博列表,微信朋友圈及其它社交类应用的好友动态展示列表等;实现了类似腾讯微博的微博列表展示功能,包含微博文本内容,表情,图片,话题和用户可点超链接等(请参见如下效果图)。该功能在实际项目开发中非常常见,除微博应用外,微信的朋友圈,陌陌、QQ空间的好友动态等也都有类似功能

  1. RecyclerView使用和嵌套问题
  2. 动态设置图片网格宽高
  3. 正则表达式的使用
  4. Linkify实现自定义超链接
  5. TextView富文本显示
  6. 点赞动画渐变动画效果

效果图

列表的item布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="5dp"android:background="@color/white"android:descendantFocusability="blocksDescendants"android:orientation="vertical"><RelativeLayout
        android:layout_width="match_parent"android:layout_height="60dp"android:layout_marginTop="10dp"android:paddingLeft="10dp"android:paddingRight="10dp"><ImageView
            android:id="@+id/iv_avatar"android:layout_width="45dp"android:layout_height="45dp"android:layout_centerVertical="true"android:background="#11000000"android:scaleType="centerCrop"/><LinearLayout
            android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="10dp"android:layout_toRightOf="@id/iv_avatar"android:orientation="vertical"><TextView
                android:id="@+id/tv_user"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="120dp"android:text="用户名"android:textColor="@color/black"android:textSize="16sp"/><TextView
                android:id="@+id/tv_user_introduction"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:singleLine="true"android:text="用户相关介绍"android:textColor="@color/item_text_secondary"android:textSize="14sp"/></LinearLayout><TextView
            android:id="@+id/tv_date"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_marginTop="5dp"android:text="0000-00-00"android:textColor="@color/item_text_secondary"android:textSize="14sp"/></RelativeLayout><TextView
        android:id="@+id/tv_content"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:layout_marginLeft="10dp"android:layout_marginTop="5dp"android:text="这是微博内容...这是微博内容..."android:textColor="@color/item_text_main"android:textSize="16sp"/><!--显示微博图片--><android.support.v7.widget.RecyclerView
        android:id="@+id/rv_weibo_images"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="10dp"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:listSelector="@color/transparent"android:visibility="gone"/><View
        android:layout_width="match_parent"android:layout_height="1px"android:background="@color/activity_bg"/><LinearLayout
        android:layout_width="match_parent"android:layout_height="40dp"android:orientation="horizontal"><FrameLayout
            android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:clickable="true"><TextView
                android:id="@+id/tv_forward"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:drawableLeft="@drawable/selector_btn_share"android:textColor="@color/item_text_secondary"android:drawablePadding="5dp"android:gravity="center"android:text="0"/></FrameLayout><FrameLayout
            android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:clickable="true"><TextView
                android:id="@+id/tv_comment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:drawableLeft="@drawable/selector_btn_comment"android:textColor="@color/item_text_secondary"android:drawablePadding="5dp"android:gravity="center"android:text="0"/></FrameLayout><LinearLayout
            android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:gravity="center"android:clickable="true"><CheckBox
                android:id="@+id/cb_like"android:layout_width="40dp"android:layout_height="40dp"android:layout_gravity="center"android:button="@color/transparent"android:checked="false"android:drawableLeft="@drawable/selector_btn_prize"android:drawablePadding="5dp"android:background="@color/transparent"android:textColor="@color/item_text_secondary"android:gravity="center"/><TextView
                android:id="@+id/tv_like"android:text="0"android:textColor="@color/item_text_secondary"android:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout></LinearLayout></LinearLayout>

item中的RecyclerView用于显示0-9张图片,根据服务器返回的图片数量不同显示的行列数和图片的大小也不同,需要动态设置,由于这个列表是RecyclerView,item中也使用了RecyclerView,这就产生了RecyclerView的嵌套

id为tv_content的TextView用于显示内容,内容中包含了表情图片和超链接

json数据格式

{"result":true,"weibo":[{"avatar":"avatar_01","comment":5,"content":"我装作看不懂的样子[呲牙][偷笑][偷笑],单身狗保重 [再见][再见] @冷笑话精选","date":1489223423501,"forward":8,"imageUrls":["pic_1","pic_2","pic_3","pic_4","pic_5","pic_6","pic_7","pic_8","pic_9"],"like":10,"user_introduction":"最冷笑话精选,每天分享笑话N枚,你的贴身开心果。","username":"冷笑话精选"}]
}

对应的实体类

public class WeChat {public boolean result;public List<WeiboEntity> weibo;public static class WeiboEntity {public String       avatar;public int          comment;public String       content;public long         date;public int          forward;public int          like;public String       user_introduction;public String       username;public List<String> imageUrls;}
}

动态设置图片宫格数

根据图片的数量,动态设置RecyclerView的列数和宽度

  • 如果图片数量为0,则隐藏RecyclerView
  • 如果图片数量为1,RecyclerView列数设为1列,宽度设为WRAP_CONTENT
  • 如果图片数量为4,RecyclerView列数设为2列,宽度设为两个图片宫格的宽度
  • 其它,RecyclerView列数设为3列,宽度设为MATCH_PARENT
// 刷新item布局中子控件的显示@Overrideprotected void onRefreshView(WeChat.WeiboBean bean, int position) {// 显示用户名tvUser.setText(bean.getUsername());// 显示用户介绍if (TextUtils.isEmpty(bean.getUser_introduction())) {tvUserIntroduction.setVisibility(View.GONE);} else {tvUserIntroduction.setVisibility(View.VISIBLE);tvUserIntroduction.setText(bean.getUser_introduction());}// 显示头像int imageResId = Global.getResId(context, bean.getAvatar());ivAvatar.setBackgroundResource(imageResId);// 微博内容// tvContent.setText(bean.getContent());EmojiUtil.setText(tvContent, bean.getContent());LinkifyUtil.addCustomLink(tvContent);LinkifyUtil.addCustomLink2(tvContent);// 发表时间tvDate.setText(Global.formatDate(bean.getDate()));// 显示微博图片int imageCount = bean.getImageUrls() == null? 0 : bean.getImageUrls().size();if (imageCount == 0) {      // 没有微博图片rvWeiboImages.setVisibility(View.GONE);} else {    // 有微博图片rvWeiboImages.setVisibility(View.VISIBLE);imageAdapter.setDatas(bean.getImageUrls()); // 刷新图片显示// 动态的指定图片宫格的宽高和RecyclerView的宽度// 1张图片 -> 1列// 4张图片 -> 2列// 其它    -> 3列ViewGroup.LayoutParams param = rvWeiboImages.getLayoutParams();if (imageCount == 1) {layoutManager.setSpanCount(1);param.width = ViewGroup.LayoutParams.WRAP_CONTENT;} else if (imageCount == 4) {layoutManager.setSpanCount(2);// 两个图片宫格的宽度param.width = Global.getGridWidth() * 2;} else {        // 3列layoutManager.setSpanCount(3);param.width = ViewGroup.LayoutParams.MATCH_PARENT;}}}

动态设置图片的大小

  • 1张图片,宫格的宽高为图片的宽高
  • 其它情况,宫格的宽高为屏幕宽度的三分之一
    // 刷新item子控件的显示@Overrideprotected void onRefreshView(String imagePath, int position) {// 动态设置图片宫格的宽高// 1张图片  ->   宫格的宽高为图片的宽高// 其它情况  ->  宫格的宽高为Global.getGridWidth()ViewGroup.LayoutParams param = super.itemView.getLayoutParams();if (super.adapter.getItemCount() == 1) {    // 一张图片// 图片资源idint imageResId = Global.getResId(context, imagePath);Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), imageResId);// 指定宫格的宽高为图片的宽高param.width = bitmap.getWidth();param.height = bitmap.getHeight();// 显示图片ivImage.setBackgroundResource(imageResId);} else {    // 多张图片// 显示宫格图片int imageResId = Global.getResId(context, imagePath);ivImage.setBackgroundResource(imageResId);param.width = Global.getGridWidth();    // 指定宫格图片的宽param.height = Global.getGridWidth();}}

TextView富文本显示

显示文本中的表情,把文本中如[呲牙][偷笑][偷笑]的文字替换成表情图片,实现TextView的富文本显示(图文混排)。需要用正则去匹配文本中是否包含表情,匹配成功,表示文本中包含表情,用ImageSpan封装表情图片,再ImageSpan将设置给SpannableString,把文本中的表示表情的文字替换掉,最后将SpannableString设置给TextView即可。

正则参考:

[高兴]    \\[([A-Za-z\u4E00-\u9FA5]+)\\]
@用户     \\@([A-Za-z0-9\u4E00-\u9FA5]+)
#话题#    \\#([A-Za-z0-9\u4E00-\u9FA5]+)\\#
public class EmojiUtil {/** 显示文本和表情 */public static void setText(TextView textView, String text) {Context context = textView.getContext();Resources resources = context.getResources();SpannableString ss = new SpannableString(text);// 正则表达式: [高兴]Pattern p = Pattern.compile("\\[([A-Za-z\u4E00-\u9FA5]+)\\]");Matcher matcher = p.matcher(ss);while (matcher.find()) {// 匹配到一个表情字符串String emoji = matcher.group();// 过滤非表情符,比如: [xxx]if (EMOJI_DATAS.containsKey(emoji)) {   // 是表情才处理// System.out.println("----------" + emoji);// 指定了一张图片Bitmap bitmap = BitmapFactory.decodeResource(resources, EMOJI_DATAS.get(emoji));bitmap = Global.createBitmap(bitmap, Global.dp2px(20));     // 图片的宽高为20dpImageSpan span = new ImageSpan(context, bitmap, ImageSpan.ALIGN_BOTTOM);int start = matcher.start();int end = matcher.end();ss.setSpan(span, start, end, 0);}}textView.setText(ss);}private static final HashMap<String, Integer> EMOJI_DATAS = new HashMap<String, Integer>();static {EMOJI_DATAS.put("[微笑]", R.drawable.smiley_0);...}
}

让文字显示颜色

/*** 让某几个文字显示颜色* @param string* @param color* @return*/private CharSequence showTextWithColor(String string,int color) {SpannableString ss = new SpannableString(string);// BackgroundColorSpan 背景色ForegroundColorSpan colorSpan = new ForegroundColorSpan(color);int end = string.indexOf("等");ss.setSpan(colorSpan, 0, end, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE);return ss;}

让图片和文字一起显示

/*** 让图片和文字一起显示* @param text* @param imageRes* @return*/private SpannableString showTextWithImage(String text,int imageRes){SpannableString ss = new SpannableString(text);Drawable drawable = getResources().getDrawable(imageRes);//设置边界
//      drawable.setBounds(0,0,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight());drawable.setBounds(0,0,20,20);ImageSpan span = new ImageSpan(drawable);int start = text.indexOf("[");int end = text.indexOf("]")+1;ss.setSpan(span, start,end,SpannableString.SPAN_INCLUSIVE_EXCLUSIVE);return ss;}

设置超链接

// 让某段文字可以被点击并跳转超链接
String text = "详情请点击<a href='http://www.baidu.com'>百度</a>";
Spanned spanned = Html.fromHtml(text);
text3.setText(spanned);
text3.setMovementMethod(LinkMovementMethod.getInstance());//设置可以点击超链接

让某段文字可以被点击并自定义点击的逻辑操作

// 让某段文字可以被点击并自定义点击的逻辑操作
String string = "王二,小明,大兵等觉得很赞";
SpannableString ss= new SpannableString(string);
MyUrlSpan urlSpan= new MyUrlSpan(string.substring(0, string.indexOf(",")));
ss.setSpan(urlSpan, 0, 2, SpannableString.SPAN_INCLUSIVE_EXCLUSIVE);
text4.setText(ss);
text4.setMovementMethod(LinkMovementMethod.getInstance());
class MyUrlSpan extends URLSpan{public MyUrlSpan(String url) {super(url);}@Overridepublic void onClick(View widget) {// 自定义点击的操作逻辑,默认实现是获取url,打开浏览器Toast.makeText(MainActivity.this, getURL(), 0).show();widget.clearFocus();}@Overridepublic void updateDrawState(TextPaint ds) {super.updateDrawState(ds);ds.setColor(Color.RED); // 设置文字颜色ds.setUnderlineText(false); // 设置是否显示下划线}
}

自定义超链接

关于TextView 网页,电话,邮箱的自动识别。设置android:autoLink=”email|web|phone|map”属性后,TextView 可自动识别电话、邮箱、网址、地图为超链接。

<TextView
        android:id="@+id/tv"android:layout_width="match_parent"android:layout_height="wrap_content"android:scrollbarStyle="insideOverlay"android:scrollbars="vertical"android:autoLink="email|web|phone|map"android:text=" 电话:13609000000,邮箱:815612738@163.com,网址:http://www.google.com " />

添加自定义超链接,把内容中如@冷笑话精选#编程##讲故事#的文本显示为超链接,高亮显示并支持点击。先使用Linkify.MatchFilter 匹配过滤器过滤内容中的超链接,TextView在显示的内容要识别链接时,调用Linkify.addLinks()

public class LinkifyUtil {/*** 添加自定义超链接*/public static void addCustomLink(TextView textView) {// @用户:Pattern pattern = Pattern.compile("\\@([A-Za-z0-9\u4E00-\u9FA5]+)\\.?");// http://www.qq.com/path?uid=1&username=xxString scheme = "weibo://user?uid=";// 匹配过滤器Linkify.MatchFilter matchFilter = new Linkify.MatchFilter() {@Overridepublic boolean acceptMatch(CharSequence s, int start, int end) {String text = s.subSequence(start, end).toString();// System.out.println("----text: " + text);if (text.endsWith(".")) { // 邮箱,不需要匹配return false;} else {return true;    // 返回true会显示为超链接}}};Linkify.TransformFilter transformFilter = null;Linkify.addLinks(textView, pattern, scheme, matchFilter, transformFilter);}public static void addCustomLink2(TextView textView) {// @用户:Pattern pattern = Pattern.compile("\\#([A-Za-z0-9\u4E00-\u9FA5]+)\\#");// http://www.qq.com/path?uid=1&username=xxString scheme = "weibo://topic?uid=";// 匹配过滤器Linkify.MatchFilter matchFilter = new Linkify.MatchFilter() {@Overridepublic boolean acceptMatch(CharSequence s, int start, int end) {String text = s.subSequence(start, end).toString();System.out.println("----text: " + text);return true;}};Linkify.TransformFilter transformFilter = new Linkify.TransformFilter() {@Overridepublic String transformUrl(Matcher match, String url) {return match.group(1);}};Linkify.addLinks(textView, pattern, scheme, matchFilter, transformFilter);}
}

设置自定义的链接后,点击超链接后会出错。 因为没有找到Activity可以处理发起的Intent, 需要定义两个Activity来接收意图中的参数。

当点击超链接的时候,会调起/启动一个与Linkify.addLinks()方法中的scheme对应的Activity

public class TopicActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// weibo://user?uid=@冷笑话精选Uri uri = getIntent().getData();String topic = uri.getQueryParameter("uid");TextView textView = new TextView(this);textView.setGravity(Gravity.CENTER);textView.setTextColor(Color.RED);textView.setText(topic);textView.setTextSize(20);setContentView(textView);}}
public class UserActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// weibo://user?uid=@冷笑话精选Uri uri = getIntent().getData();String username = uri.getQueryParameter("uid");TextView textView = new TextView(this);textView.setGravity(Gravity.CENTER);textView.setTextColor(Color.GRAY);textView.setText(username);textView.setTextSize(20);setContentView(textView);}}

在清单文件中配置以上Activity,给Activity设置action、category、data

<!--点击用户链接时,要调起该Activity-->
<activity android:name=".ui.activity.UserActivity"><intent-filter><action android:name="android.intent.action.VIEW"/><category android:name="android.intent.category.DEFAULT"/><data android:scheme="weibo" android:host="user"/></intent-filter>
</activity>
<activity android:name=".ui.activity.TopicActivity"><intent-filter><action android:name="android.intent.action.VIEW"/><category android:name="android.intent.category.DEFAULT"/><data android:scheme="weibo" android:host="topic"/></intent-filter>
</activity>

点赞动画

在MainActivity的布局文件中,有一个TextView,是用来执行点赞后的+1的动画(向上平移,透明度变小,放大)。 该控件开始时隐藏,执行点赞动画时,注意不是列表项中的控件执行动画。

// WeiboHolder.java
cbLike.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {if (isChecked) {// 获取当前点击控件相对于窗口的所在位置int[] locations = new int[2];tvLike.getLocationInWindow(locations);((MainActivity) context).animateUp(locations);}}
});
public void animateUp(int[] locations) {// 减去状态栏高度24dpint currentY = locations[1] - Global.dp2px(24);tvLike.setVisibility(View.VISIBLE);tvLike.setTranslationX(locations[0]);tvLike.setTranslationY(currentY);tvLike.setScaleY(1);tvLike.setScaleX(1);tvLike.setAlpha(1f);// 往上移动30dpint top = currentY - Global.dp2px(30);tvLike.animate().alpha(0).translationY(top).setInterpolator(new DecelerateInterpolator()).scaleX(1.2f).scaleY(1.2f).setDuration(1000);
}

代码:https://github.com/JackChan1999/WeChatDemo

微信朋友圈,QQ空间,微博等列表展示的功能实现相关推荐

  1. js+插件实现代码复制及动态生成二维码扫描、分享到朋友圈QQ空间功能

    1.代码复制功能需要插件支持ZeroClipbroad,引入js:jquery.zclip.min.js;ZeroClipboard.js;ZeroClipboard.swf 2.jsp功能代码: & ...

  2. Compose版来啦,高仿微信朋友圈大图缩放、切换、预览功能

    最近在学习Jetpack Compose,想着能否用Jetpack Compose实现微信一些重要界面以及功能.好消息是已经实现了微信聊天界面相关功能以及交互,最近又搞了搞朋友圈的整体交互,网上看了看 ...

  3. android 文字点击展开,仿微信朋友圈,文字展开全文,全文收起功能

    android:id="@+id/tv_expand_or_fold" android:layout_width="wrap_content" android: ...

  4. 仿微信朋友圈,文字展开全文,全文收起功能

    简单操作4步即可实现文字展开收起功能: 1.布局写2个Textview 一个是内容 一个是按钮(收起和全文) <TextView android:layout_marginTop="@ ...

  5. 微信朋友圈技术实现设想

    前提 微信朋友圈是我们每天都在用的功能, 但是如果让你来实现一个微信朋友圈, 你会如何做呢? 我来简单设想一下. 实现功能 发朋友圈 评论动态 查看朋友圈(只能查看好友的) 查看评论(只能查看共同好友 ...

  6. 评论安装_朋友圈怎么语音评论?微信朋友圈语音评论捷径下载安装使用教程

    微信朋友圈评论默认是不支持图片和语音评论的,不过可以通过一些链接的方式间接实现.之前小编为大家分享过"iPhone微信朋友圈图片评论方法",今天借助类似的原理,为大家分享一下微信朋 ...

  7. 写一个微信朋友圈的测试用例

    由于第一次编写测试用例,若有不完整的地方,欢迎指正!!! 首先将微信朋友圈分成以下模块: 1.点赞功能 1.网速对点赞的影响 2.点赞的人个数显示是否正确 3.共同好友能否看到点赞状态 4.能否显示点 ...

  8. 你的微信朋友圈让你焦虑了吗?

    小咖导读:不管休假不休假,我们都已经越来越依赖于微信,坐下来刷刷朋友圈,几乎是很多人每天打发碎片化时间的最好方式.当天长日久,刷朋友圈的时候你有没有不快乐?技术在给每个人提供便利和效率的时候,社会性又 ...

  9. 仿微信朋友圈选择图片

    仿微信朋友圈选择图片 该版本实现了如下功能: 1.从相册选择图片,对图片进行了缓存处理,选择图片的时候,图片不会出现OOM 2.加入了拍照功能 3.加入了图库功能,可以让你的图片滚动起来了,如果你想使 ...

最新文章

  1. mysql存储base64位用什么类型_了解什么是存储引擎引发的MySQL面试3连问
  2. Visual Studio 2017 最新全量离线下载方法[有惊喜]
  3. dofuscator C# 混淆器 原来如此
  4. LINUX CP 命令强制覆盖功能开启/关闭
  5. Crypto API 学习笔记一
  6. UI培训之零基础如何自学UI设计?
  7. 将含有自定义代码的Infopath模板发布到Sharepoint表单库中
  8. 漫画:“架构师”小赵的故事
  9. Java面试全集(中)
  10. 科学计算器 java_用Java编写的标准计算器、科学计算器、时间转换。
  11. RHEL 7 新特性
  12. Oracle-select...into...from语句的使用
  13. API接口设计的五大公共参数
  14. 2022国赛新大陆物联网Ubuntu系统维护(中职)
  15. XML采用Boost::regex解析实例
  16. 重新理解函数空间(上)
  17. mysql简单数据库定期备份
  18. Flash/Flex学习笔记(43):动量守恒与能量守恒
  19. 虚拟机下的Linux网络连接方法(NAT)
  20. cherrytree笔记_如何使用CherryTree做笔记

热门文章

  1. 虚拟实验室中的事务管理系统(一、概述)
  2. 回文数的JAVA程序
  3. feng作品推荐あかね色に染まる坂 染成茜色的坂道 (含下载、攻略)
  4. 云计算对中小企业的大冲击
  5. 使用迭代器从map或vector中删除元素
  6. Host XXX is not allowed to connect to this MySql 远程连接
  7. json串反转义(消除反斜杠)-- 转载
  8. 关于YOLO算法的备忘
  9. [笔记]C#基础入门(五)——算法:交换
  10. 数据库、记录、字段、文档