具体Demo见GitHub:RichTextDemo
无图无真相,下图中的所有内容全在一个TextView
中展示。
TextView实现图文混排
主要由SpannableString.setSpan()
和ImageSapn
实现TextView
显示图片功能,核心代码如下:
String text = "图文混排内容";
SpannableString spannableString = new SpannableString(text);
Drawable drawable = 来自本地或者网络的图片;
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);
spannableString.setSpan(imageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString);
其中spannableString.setSpan(imageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
将text
中图片对应的文本,替换成相应的文图片,实现图文混排。ImageSpan
中需传入的Drawable
可以读取本地图片,或者从网络获取。
详细代码如下,具体说明看代码注释:
其中简书中正则表达式显示有问题,正确的为
正则表达式.png
private void setRichText(Context context) {
float imageSize = textView.getTextSize();
//图文混排的text,这里用"[]"标示图片
String text = "这是图文混排的例子:\n网络图片:"
+ "[http://hao.qudao.com/upload/article/20160120/82935299371453253610.jpg][http://b.hiphotos.baidu.com/zhidao/pic/item/d6ca7bcb0a46f21f27c5c194f7246b600d33ae00.jpg]"
+ "\n本地图片:" + "[哈哈][泪][多肉][多肉2]";
SpannableString spannableString = new SpannableString(text);
//匹配"[(除了']'任意内容)]"的正则表达式,获取网络图片和本地图片替换位置
//简书中正则表达式显示有问题,正确如上图
Pattern pattern = Pattern.compile("\\[[^\\]]+\\]");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
ImageSpan imageSpan;
//匹配的内容,例如[http://hao.qudao.com/upload/article/20160120/82935299371453253610.jpg]或[哈哈]
String group = matcher.group();
if (group.contains("http")) {
//网络图片
//获取图片url(去掉'['和']')
String url = group.substring(1, group.length() - 1);
//异步获取网络图片
Drawable drawableFromNet = new URLImageParser(textView, context, (int) imageSize).getDrawable(url);
imageSpan = new ImageSpan(drawableFromNet, ImageSpan.ALIGN_BASELINE);
//设置网络图片
spannableString.setSpan(imageSpan, matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
//本地图片
if (localIconMap.get(group) != null) {
//获取本地图片Drawable
Drawable drawableFromLocal = context.getResources().getDrawable(localIconMap.get(group));
//获取图片宽高比
float ratio = drawableFromLocal.getIntrinsicWidth() * 1.0f / drawableFromLocal.getIntrinsicHeight();
//设置图片宽高
drawableFromLocal.setBounds(0, 0, (int) (imageSize * ratio), (int)(imageSize));
imageSpan = new ImageSpan(drawableFromLocal, ImageSpan.ALIGN_BASELINE);
//设置本地图片
spannableString.setSpan(imageSpan, matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
textView.setText(spannableString);
}
private void initData(Context context) {
ResourceUtils utils = ResourceUtils.getInstance(context);
localIconMap = new HashMap<>();
//获取本地图片标示对应的图片ID(例如[哈哈]对应的R.drawable.haha)
localIconMap.put(utils.getString("haha"), utils.getDrawableId("haha"));
localIconMap.put(utils.getString("lei"), utils.getDrawableId("lei"));
localIconMap.put(utils.getString("duorou"), utils.getDrawableId("duorou"));
localIconMap.put(utils.getString("duorou2"), utils.getDrawableId("duorou2"));
}
其中URLImageParser代码如下:
public class URLImageParser {
private Context mContext;
private TextView mTextView;
private int mImageSize;
/**
*
* @param textView 图文混排TextView
* @param context
* @param imageSize 图片显示高度
*/
public URLImageParser(TextView textView, Context context, int imageSize) {
mTextView = textView;
mContext = context;
mImageSize = imageSize;
}
public Drawable getDrawable(String url) {
URLDrawable urlDrawable = new URLDrawable();
new ImageGetterAsyncTask(mContext, url, urlDrawable).execute(mTextView);
return urlDrawable;
}
public class ImageGetterAsyncTask extends AsyncTask<TextView, Void, Bitmap> {
private URLDrawable urlDrawable;
private Context context;
private String source;
private TextView textView;
public ImageGetterAsyncTask(Context context, String source, URLDrawable urlDrawable) {
this.context = context;
this.source = source;
this.urlDrawable = urlDrawable;
}
@Override
protected Bitmap doInBackground(TextView... params) {
textView = params[0];
try {
//下载网络图片,以下是使用Picasso和Glide获取网络图片例子,也可以其他方式下载网络图片
// 使用Picasso获取网络图片Bitmap
return Picasso.with(context).load(source).get();
// 使用Glide获取网络图片Bitmap(使用Glide获取图片bitmap还有待研究)
// return Glide.with(context).load(source).asBitmap().fitCenter().into(mImageSize * 3, mImageSize * 3).get();
} catch (Exception e) {
return null;
}
}
@Override
protected void onPostExecute(final Bitmap bitmap) {
try {
//获取图片宽高比
float ratio = bitmap.getWidth() * 1.0f / bitmap.getHeight();
Drawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
bitmapDrawable.setBounds(0, 0, (int) (mImageSize * ratio), mImageSize);
//设置图片宽、高(这里传入的mImageSize为字体大小,所以,设置的高为字体大小,宽为按宽高比缩放)
urlDrawable.setBounds(0, 0, (int) (mImageSize * ratio), mImageSize);
urlDrawable.drawable = bitmapDrawable;
//两次调用invalidate才会在异步加载完图片后,刷新图文混排TextView,显示出图片
urlDrawable.invalidateSelf();
textView.invalidate();
} catch (Exception e) {
/* Like a null bitmap, etc. */
}
}
}
}
URLDrawable代码如下:
public class URLDrawable extends BitmapDrawable {
// the drawable that you need to set, you could set the initial drawing
// with the loading image if you need to
protected Drawable drawable;
@Override
public void draw(Canvas canvas) {
// override the draw to facilitate refresh function later
if(drawable != null) {
drawable.draw(canvas);
}
}
}
ResourceUtils代码如下:
public class ResourceUtils {
private static ResourceUtils resourceUtils;
private static Context context;
private Resources resources;
private String packageName;
public static ResourceUtils getInstance(Context context) {
if (resourceUtils == null) {
synchronized (ResourceUtils.class) {
resourceUtils = new ResourceUtils(context);
}
}
return resourceUtils;
}
public ResourceUtils(Context context) {
this.context = context;
resources = context.getResources();
packageName = context.getPackageName();
}
public int getStringId(String name) {
try {
//对应values:strings.xml文件
return this.resources.getIdentifier(name, "string", packageName);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
public String getString(String name) {
try {
return this.resources.getString(getStringId(name));
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
public int getDrawableId(String name) {
try {
//对应drawable-***文件夹中的图片
return this.resources.getIdentifier(name, "drawable", packageName);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
具体Demo见GitHub:RichTextDemo
参考:
Android之Glide获取图片Path、Bitmap用法
Display images on Android using TextView and Html.ImageGetter asynchronously?
Android HTML ImageGetter as AsyncTask
Android ImageGetter images overlapping text