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

内容摘要

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

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

效果图

朋友圈列表的实现

列表的item布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<?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数据格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"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":"冷笑话精选"
}
]
}

对应的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 刷新item布局中子控件的显示
@Override
protected 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张图片,宫格的宽高为图片的宽高
  • 其它情况,宫格的宽高为屏幕宽度的三分之一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 刷新item子控件的显示
@Override
protected void onRefreshView(String imagePath, int position) {
// 动态设置图片宫格的宽高
// 1张图片 -> 宫格的宽高为图片的宽高
// 其它情况 -> 宫格的宽高为Global.getGridWidth()
ViewGroup.LayoutParams param = super.itemView.getLayoutParams();
if (super.adapter.getItemCount() == 1) { // 一张图片
// 图片资源id
int 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();
}
}

自定义FlowLayout实现九宫格图片控件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class FlowLayout extends ViewGroup {
private float mVerticalSpacing;
private float mHorizontalSpacing;
public FlowLayout(Context context) {
super(context);
init(null, 0, 0);
}
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0, 0);
}
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
final Context context = getContext();
final Resources resources = getResources();
final float density = resources.getDisplayMetrics().density;
int vSpace = (int) (4 * density);
int hSpace = vSpace;
if (attrs != null) {
// Load attributes
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.FlowLayout, defStyleAttr, defStyleRes);
// Load clip touch corner radius
vSpace = a.getDimensionPixelOffset(R.styleable.FlowLayout_verticalSpace, vSpace);
hSpace = a.getDimensionPixelOffset(R.styleable.FlowLayout_horizontalSpace, hSpace);
a.recycle();
}
setVerticalSpacing(vSpace);
setHorizontalSpacing(hSpace);
}
public void setHorizontalSpacing(float pixelSize) {
mHorizontalSpacing = pixelSize;
}
public void setVerticalSpacing(float pixelSize) {
mVerticalSpacing = pixelSize;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int selfWidth = resolveSize(0, widthMeasureSpec);
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int childLeft = paddingLeft;
int childTop = paddingTop;
int lineHeight = 0;
//通过计算每一个子控件的高度,得到自己的高度
for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
View childView = getChildAt(i);
LayoutParams childLayoutParams = childView.getLayoutParams();
childView.measure(
getChildMeasureSpec(widthMeasureSpec, paddingLeft + paddingRight,
childLayoutParams.width),
getChildMeasureSpec(heightMeasureSpec, paddingTop + paddingBottom,
childLayoutParams.height));
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
lineHeight = Math.max(childHeight, lineHeight);
if (childLeft + childWidth + paddingRight > selfWidth) {
childLeft = paddingLeft;
childTop += mVerticalSpacing + lineHeight;
lineHeight = childHeight;
} else {
childLeft += childWidth + mHorizontalSpacing;
}
}
int wantedHeight = childTop + lineHeight + paddingBottom;
setMeasuredDimension(selfWidth, resolveSize(wantedHeight, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int myWidth = r - l;
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int paddingRight = getPaddingRight();
int childLeft = paddingLeft;
int childTop = paddingTop;
int lineHeight = 0;
//根据子控件的宽高,计算子控件应该出现的位置。
for (int i = 0, childCount = getChildCount(); i < childCount; ++i) {
View childView = getChildAt(i);
if (childView.getVisibility() == View.GONE) {
continue;
}
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
lineHeight = Math.max(childHeight, lineHeight);
if (childLeft + childWidth + paddingRight > myWidth) {
childLeft = paddingLeft;
childTop += mVerticalSpacing + lineHeight;
lineHeight = childHeight;
}
childView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
childLeft += childWidth + mHorizontalSpacing;
}
}
}

布局文件

1
2
3
4
5
6
7
8
9
<com.jackchan.booster.widget.FlowLayout
android:id="@+id/fl_image"
android:layout_marginTop="@dimen/lay_8"
android:paddingBottom="2dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:horizontalSpace="4dp"
app:verticalSpace="4dp" />

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void getFlowLayoutImages() {
flowLayout.removeAllViews();
if (mImageLists != null && mImageLists.size() > 0) {
flowLayout.setVisibility(View.VISIBLE);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams((int) TDevice.dip2px(HMApp.context(), 85)
, (int) TDevice.dip2px(HMApp.context(), 85));
for (int i = 0; i < mImageLists.size(); i++) {
final int currentIndex = i;
ImageView imageView = new ImageView(HMApp.context());
imageView.setLayoutParams(layoutParams);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
final String path = mImageLists.get(i);
imageView.setTag(R.id.iv_tweet_image, i);
ImageUtil.loadImage(imageView,path);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(mContext, PicDetailActivity.class);
intent.putExtra(JsBinder.CUR_IMG_URL, path);
intent.putExtra(ConstantUtils.ALL_IMG_URL, mImageLists);
mContext.startActivity(intent);
}
});
flowLayout.addView(imageView);
}
} else {
flowLayout.setVisibility(View.GONE);
}

TextView富文本显示

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

正则参考:

1
2
3
[高兴] \\[([A-Za-z\u4E00-\u9FA5]+)\\]
@用户 \\@([A-Za-z0-9\u4E00-\u9FA5]+)
#话题# \\#([A-Za-z0-9\u4E00-\u9FA5]+)\\#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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)); // 图片的宽高为20dp
ImageSpan 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);
...
}
}

让文字显示颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 让某几个文字显示颜色
* @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;
}

让图片和文字一起显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 让图片和文字一起显示
* @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;
}

设置超链接

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

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

1
2
3
4
5
6
7
// 让某段文字可以被点击并自定义点击的逻辑操作
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());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyUrlSpan extends URLSpan{
public MyUrlSpan(String url) {
super(url);
}
@Override
public void onClick(View widget) {
// 自定义点击的操作逻辑,默认实现是获取url,打开浏览器
Toast.makeText(MainActivity.this, getURL(), 0).show();
widget.clearFocus();
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(Color.RED); // 设置文字颜色
ds.setUnderlineText(false); // 设置是否显示下划线
}
}

自定义超链接

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

1
2
3
4
5
6
7
8
<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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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=xx
String scheme = "weibo://user?uid=";
// 匹配过滤器
Linkify.MatchFilter matchFilter = new Linkify.MatchFilter() {
@Override
public 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=xx
String scheme = "weibo://topic?uid=";
// 匹配过滤器
Linkify.MatchFilter matchFilter = new Linkify.MatchFilter() {
@Override
public 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() {
@Override
public String transformUrl(Matcher match, String url) {
return match.group(1);
}
};
Linkify.addLinks(textView, pattern, scheme, matchFilter, transformFilter);
}
}

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

ActivityNotFoundException

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TopicActivity extends AppCompatActivity {
@Override
protected 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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class UserActivity extends AppCompatActivity {
@Override
protected 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--点击用户链接时,要调起该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的动画(向上平移,透明度变小,放大)。 该控件开始时隐藏,执行点赞动画时,注意不是列表项中的控件执行动画。

1
2
3
4
5
6
7
8
9
10
11
12
// WeiboHolder.java
cbLike.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// 获取当前点击控件相对于窗口的所在位置
int[] locations = new int[2];
tvLike.getLocationInWindow(locations);
((MainActivity) context).animateUp(locations);
}
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void animateUp(int[] locations) {
// 减去状态栏高度24dp
int 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);
// 往上移动30dp
int 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

坚持原创技术分享,您的支持将鼓励我继续创作!