本文主要讲在Textview中获取指定文字的位置,最后,附带一点文字宽高的测量。
下面,我会给出全部源码。自己建个demo,复制进去就能跑起来。
先强调一下,不建议在ListView中使用。最好是在一个单独展示Textview的界面中使用。
先上效果图:
需求及说明:给出一段文字,里面可能包含链接或表情。同时,也有要匹配的关键字,最后,在关键字处展示一个图片,用于强调这个关键字。对应关键字,也可能有多种情况,如:@人名、股票代码等。这里ABC和000001代替。最后,我要在第一个出现的000001位置展示图片。
在源码开始前,先看看项目结构:
说明:
CHEN是工具类
DataBean是listView中,list里面要放的数据的对象
KeyBean是关键字对象
Mainactivity_1是单纯展示一个Textview展示效果图中的内容
Mainactivity_2是把1中的功能放到ListView中(有问题,到现在为止,我没解决,下面会详细说)
Mainactivity_3是文字宽高的测量
MyTextView是可以匹配链接和表情的自定义Textview
PicImageView是自定义的ImageView,是用于自动展示帧动画的ImageView
ViewHolder是ListView中用的
代码:
先上工具相关的准备东西:
CHEN
package com.chen.demo;
import android.content.Context;
public class CHEN {
/**
* 网址要被替换成的文字
*/
public static String REPLACEMENT_STRING = "*点击链接";
// /**
// * 匹配网址的正则表达式。以http://为例
// */
// public static String urlRegex = "(http://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)";
/**
* 匹配网址的正则表达式。有http://、https://、ftp://这3中开头的
*/
public static String urlRegex = "((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)|([a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>,]*)?)";
public static int sp2px(float spValue, Context context) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
}
DataBean
package com.chen.demo;
public class DataBean {
//关键字的内容
private String content;
//类型,用于区分是否需要在关键字位置显示图片
private String type;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
KeyBean
package com.chen.demo;
public class KeyBean {
//关键字的内容
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
ViewHolder
package com.chen.demo;
import android.util.SparseArray;
import android.view.View;
public class ViewHolder {
private ViewHolder(){
}
public static <T extends View> T get(View convertView, int id){
SparseArray<View> viewHolder=(SparseArray<View>) convertView.getTag();
if(viewHolder==null){
viewHolder=new SparseArray<View>();
convertView.setTag(viewHolder);
}
View childView=viewHolder.get(id);
if(childView==null){
childView=convertView.findViewById(id);
viewHolder.put(id, childView);
}
return (T)childView;
}
}
自定义View
MyTextView
这里面定义了2种方法,一种是直接展示内容,关于关键字的位置,需要通过另外的方法获取;一种是展示完后直接返回关键字的位置
package com.chen.demo;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MyTextView extends TextView {
private Context context = null;
private int firstKeyWordPosition = -1;
public MyTextView(Context context) {
super(context);
this.context = context;
}
public MyTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
}
public void showContent_1(String str, ArrayList<KeyBean> keyBeanList) {
String content = str;
//处理匹配的url
Pattern p = Pattern.compile(CHEN.urlRegex);
Matcher m = p.matcher(content);
ArrayList<String> urlList = new ArrayList<String>();
while (m.find()) {
String urlStr = m.group();
if (urlStr.contains("http://") || urlStr.contains("ftp://")) {
//如果末尾有英文逗号或者中文逗号等,就去掉
while (urlStr.endsWith(",") || urlStr.endsWith(",") || urlStr.endsWith(".") || urlStr.endsWith("。") || urlStr.endsWith(";") || urlStr.endsWith(";") || urlStr.endsWith("!") || urlStr.endsWith("!") || urlStr.endsWith("?") || urlStr.endsWith("?")) {
urlStr = urlStr.substring(0, urlStr.length() - 1);
}
urlList.add(urlStr);
content = content.replace(urlStr, CHEN.REPLACEMENT_STRING);
}
}
SpannableString spannableString = new SpannableString(content);
//处理表情相关
String emoji_string = "\\[(.+?)\\]";
Pattern emoji_patten = Pattern.compile(emoji_string);
Matcher matcher = emoji_patten.matcher(content);
while (matcher.find()) {
Drawable drawable = context.getResources().getDrawable(R.mipmap.emoji_weixiao);
drawable.setBounds(0, 0, CHEN.sp2px(25, context), CHEN.sp2px(25, context));
ImageSpan imgSpan = new ImageSpan(drawable);
spannableString.setSpan(imgSpan, matcher.start(),
matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
//表情相关处理结束
content = spannableString.toString();
//处理链接
if (urlList.size() > 0) {
int urlStartNew = 0;
int urlStartOld = 0;
String urlTemp = content;
for (int i = 0; i < urlList.size(); i++) {
final String regexUrl = urlList.get(i);
spannableString.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(TextPaint ds) {
// TODO Auto-generated method stub
super.updateDrawState(ds);
ds.setColor(0xff2097D9);
ds.setUnderlineText(false);