工作太忙,不做过多的解释了,核心是用到了 SpannableStringBuilder  Glide  和 Rxjava

直接上代码了,就两个类。

public class ImageSpanAsyncLoad {

    public ImageSpanAsyncLoad() {

    }

    @SuppressLint("StaticFieldLeak")
public void displayTextAndImage(List<SkuDeliveryBean> skuDelivery, final String title, final TextView textView) { if (TextUtils.isEmpty(title)) {
return;
} final StringBuilder stringBuilder;
if (skuDelivery != null && skuDelivery.size() > 0) {
stringBuilder = new StringBuilder(" " + title);
} else {
stringBuilder = new StringBuilder(title);
}
textView.setText(stringBuilder); // List<String> tagPathList = getProductDelivery(skuDelivery);
if (skuDelivery == null || skuDelivery.size() <= 0) {
return;
} // 创建下载任务
List<Observable<ResultTask>> tempTaskList = new ArrayList<>();
for (int i = 0; i < skuDelivery.size(); i++) {
DownloadTask task = new DownloadTask(i, skuDelivery.get(i).getSkuDeliveryUrl());
task.width = skuDelivery.get(i).getWidth();
task.height = skuDelivery.get(i).getHeight();
tempTaskList.add(createDownaloadTask(task));
stringBuilder.insert(0, " ");
} // 合并任务
Observable.mergeDelayError(tempTaskList)
.subscribeOn(Schedulers.newThread())
.unsubscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResultTask>() { Disposable mD;
List<ResultTask> temp = new ArrayList<>(); @Override
public void onSubscribe(Disposable d) {
mD = d;
} @Override
public void onNext(ResultTask resultTask) {
temp.add(resultTask);
} @Override
public void onError(Throwable e) { } @Override
public void onComplete() {
if (mD != null) {
mD.dispose();
} // 排序
Collections.sort(temp, new Comparator<ResultTask>() {
@Override
public int compare(ResultTask o1, ResultTask o2) {
return o1.position - o2.position;
}
}); // 开始绘制
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(stringBuilder.toString());
for (int i = 0; i < temp.size(); i++) {
ResultTask task = temp.get(i);
float radio = (float) SizeUtils.dp2px(task.height) / task.drawable.getIntrinsicHeight();
task.drawable.setBounds(0, 0, (int) (task.drawable.getIntrinsicWidth() * radio), SizeUtils.dp2px(task.height));
CenterAlignImageSpan imageSpan = new CenterAlignImageSpan(task.drawable);
sb.setSpan(imageSpan, i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
textView.setText(sb);
}
}); } // 创建下载任务
private Observable<ResultTask> createDownaloadTask(DownloadTask downloadTask) { Observable<ResultTask> temp; temp = Observable.just(downloadTask)
.observeOn(Schedulers.newThread())
.flatMap(new Function<DownloadTask, ObservableSource<ResultTask>>() {
@Override
public ObservableSource<ResultTask> apply(DownloadTask downloadTask) throws Exception { ResultTask resultTask = new ResultTask();
resultTask.position = downloadTask.position;
resultTask.width = downloadTask.width;
resultTask.height = downloadTask.height; // 下图片
if (downloadTask.drawable != null) {
resultTask.drawable = downloadTask.drawable;
} else {
resultTask.drawable = GlideApp.with(BaseApplication.getInstance())
.asDrawable()
.load(downloadTask.imgUrl)
.submit()
.get();
} return Observable.just(resultTask);
}
}); return temp; } /**
* 设置标签
*
* @param skuDelivery 全球购 次日达 等网络图片
* @param title 标题
* @param textView view
* @param tag 自营非自营等
*/
@SuppressLint("StaticFieldLeak")
public void displayTextAndImage(List<SkuDeliveryBean> skuDelivery, final String title, final TextView textView, final String tag) { if (TextUtils.isEmpty(title)) {
return;
} final StringBuilder stringBuilder;
if (skuDelivery != null && skuDelivery.size() > 0) {
stringBuilder = new StringBuilder(" " + title);
} else {
stringBuilder = new StringBuilder(title);
}
textView.setText(stringBuilder);
// 创建下载任务
List<Observable<ResultTask>> tempTaskList = new ArrayList<>();
int j = 0;
//有标签手动设置drawable
if (!TextUtils.isEmpty(tag)) {
//R.layout.tag是每个标签的布局
View view = LayoutInflater.from(textView.getContext()).inflate(R.layout.tag, null);
TextView tv_tag = view.findViewById(R.id.tv_tag);
tv_tag.setText(tag);
view.setDrawingCacheEnabled(true);
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
Bitmap bitmap = ConvertUtils.view2Bitmap(view);
Drawable drawable = ConvertUtils.bitmap2Drawable(bitmap);
tempTaskList.add(createDownaloadTask(new DownloadTask(0, drawable)));
stringBuilder.insert(j, " ");
j = j + 1;
view.destroyDrawingCache();
} List<String> tagPathList = getProductDelivery(skuDelivery);
if (tagPathList != null && tagPathList.size() > 0) {
for (int i = 0; i < tagPathList.size(); i++) {
if (TextUtils.isEmpty(tagPathList.get(i))) {
continue;
}
tempTaskList.add(createDownaloadTask(new DownloadTask(i + 1, tagPathList.get(i))));
stringBuilder.insert(j, " ");
}
} if (tempTaskList.size() <= 0) {
return;
}
// 合并任务
Observable.mergeDelayError(tempTaskList)
.subscribeOn(Schedulers.newThread())
.unsubscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResultTask>() { Disposable mD;
List<ResultTask> temp = new ArrayList<>(); @Override
public void onSubscribe(Disposable d) {
mD = d;
} @Override
public void onNext(ResultTask resultTask) {
temp.add(resultTask);
} @Override
public void onError(Throwable e) { } @Override
public void onComplete() {
if (mD != null) {
mD.dispose();
} // 排序
Collections.sort(temp, new Comparator<ResultTask>() {
@Override
public int compare(ResultTask o1, ResultTask o2) {
return o1.position - o2.position;
}
}); // 开始绘制
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(stringBuilder.toString());
for (int i = 0; i < temp.size(); i++) {
ResultTask task = temp.get(i);
float dpValue = 24;
if (!TextUtils.isEmpty(tag) && i == 0) {
dpValue = 16;
}
float radio = (float) SizeUtils.dp2px(dpValue) / task.drawable.getIntrinsicHeight();
task.drawable.setBounds(0, 0, (int) (task.drawable.getIntrinsicWidth() * radio), SizeUtils.dp2px(dpValue));
CenterAlignImageSpan imageSpan = new CenterAlignImageSpan(task.drawable);
sb.setSpan(imageSpan, i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
textView.setText(sb);
}
}); } // 下载任务类
private static class DownloadTask {
private int position;
private String imgUrl;
private int width;
private int height;
private Drawable drawable; public DownloadTask(int position, String imgUrl) {
this.position = position;
this.imgUrl = imgUrl;
} public DownloadTask(int position, Drawable drawable) {
this.position = position;
this.drawable = drawable;
}
} // 承载任务的类
private static class ResultTask {
private int position;
private Drawable drawable;
private int width;
private int height;
} /**
* 转化标签
*/
private List<String> getProductDelivery(List<SkuDeliveryBean> skuDelivery) {
List<String> temp = new ArrayList<>();
if (skuDelivery != null) {
for (SkuDeliveryBean bean : skuDelivery) {
if (bean == null) {
continue;
}
temp.add(bean.getSkuDeliveryUrl());
}
}
return temp;
}
}

ImageSpan

public class CenterAlignImageSpan extends ImageSpan {

    public CenterAlignImageSpan(Drawable drawable) {
super(drawable);
} @Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
Drawable d = getDrawable();
Rect rect = d.getBounds();
if (fm != null) {
Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
int fontHeight = fmPaint.bottom - fmPaint.top;
int drHeight = rect.bottom - rect.top; int top = drHeight / 2 - fontHeight / 4;
int bottom = drHeight / 2 + fontHeight / 4; fm.ascent = -bottom;
fm.top = -bottom;
fm.bottom = top;
fm.descent = top;
}
return rect.right;
} @Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
Drawable b = getDrawable();
canvas.save();
int transY = 0;
transY = ((bottom - top) - b.getBounds().bottom) / 2 + top;
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
} }

Android-textview图文混排(网络图片)的更多相关文章

  1. TextView图文混排

    大家都知道,textView有一个setCompoundDrawables的方法来设置上下左右位置的图标,当然,也可以在xml布局文件中设置,然而问题来了,假如我们把图标放在左边,当我们让TextVi ...

  2. TextView + Spanned实现图文混排以及图片点击交互

    最近要实现图文混排的需求,webview过大,所以想到了用SpannableStringBuilder来实现. 不过参考了大量国内文章,大多数是教你如何实现图文混排,并没有提及图片点击交互的.有翻阅了 ...

  3. Android TextView图文混合编排

    Android TextView图文混合编排 实现技术细节不难,两个要点:1.html代码的混合编写.2,重写ImageGetter.例如:布局: <?xml version="1.0 ...

  4. Android 自绘TextView解决提前换行问题,支持图文混排

    先看下效果图: 上面是MTextView,下面是默认的TextView. 一.原因 用最简单的全英文句子为例,如果有一个很长的单词,这一行剩余的空间显示不下了,那么规则就是不打断单词,而是把整个单词丢 ...

  5. Android TextView中图文混排设置行间距导致高度不一致问题解决

    最近项目中需要实现一个评论带表情的功能,刚开始一切顺利,非常easy,突然有一天发现文字跟表情混排的时候,TextView中图文高度不一致,excuse...什么鬼,之前明明测试过图文混排,不存在这个 ...

  6. Android中Textview显示Html,图文混排,支持图片点击放大

    本文首发于网易云社区 对于呈现Html文本来说,Android提供的Webview控件可以得到很好的效果,但使用Webview控件的弊端是效率相对比较低,对于呈现简单的html文本的话,杀鸡不必使用牛 ...

  7. android:怎样在TextView实现图文混排

    我们通常在TextView文本中设置文字.但是怎样设置图文混排呢? 我就在这里写一个样例 .我们须要用到一点简单的HTML知识 在TextView中预订了一些类似HTML的标签,通过标签能够使Text ...

  8. 使用android SpannableStringBuilder实现图文混排

    项目开发中需要实现这种效果 多余两行,两行最后是省略号,省略号后面是下拉更多 之前用过的是Html.fromHtml去处理图文混排的,仅仅是文字后图片或者文字颜色字体什么的, 但是这里需要在最后文字的 ...

  9. 使用android SpannableStringBuilder实现图文混排,看到许多其他

    项目开发需要达到这种效果 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZmFuY3lsb3ZlamF2YQ==/font/5a6L5L2T/fontsiz ...

  10. Android自动解析html带图片,实现图文混排

    在android中,如何将html代码转换为text,然后显示在textview中呢,有一个简单直接的方法: textView.setText(Html.fromHtml(content)); 然而用 ...

随机推荐

  1. 在IIS Express中配置和启动web site

    Step 1 进入 C:\Users\[User]\Documents\IISExpress\config . 编辑applicationhost.config文件 加入站点信息如下: Step2:进 ...

  2. ASP.NET Core 2.1 使用Docker运行

    重要提示,本文为 ASP.NET Core 2.1 如果你是 2.2 那么请将文中的镜像换为 microsoft/dotnet:2.2.0-aspnetcore-runtime 即可,其他操作一样 1 ...

  3. mysql 开发进阶篇系列 13 锁问题(关于表锁,死锁示例,锁等待设置)

    一. 什么时候使用表锁 对于INNODB表,在绝大部分情况下都应该使用行锁.在个别特殊事务中,可以考虑使用表锁(建议). 1. 事务需要更新大部份或全部数据,表又比较大,默认的行锁不仅使这个事务执行效 ...

  4. 全网最详细的最新稳定OSSEC搭建部署(ossec-server(CentOS6.X)和ossec-agent(CentOS6.X))(图文详解)

    不多说,直接上干货! 前言 写在前面的话,网上能够找到一些关于ossec方面的资料,虽然很少,但是总比没有强,不过在实际的使用过程中还是会碰到许多稀奇古怪的问题.整理整理我的使用过程,就当做一篇笔记吧 ...

  5. Mybatis解析mapper

    众所周知,接口是不能被实例化的,但是日常开发中,我们经常能直接使用dao层对象的方法,这又是为什么呢. 带着这些问题,我们看下mybatis内部做了那些操作. Mapper解析 上文我们描述了myba ...

  6. Mybatis 源码简述

    转载请注明来自:http://www.cnblogs.com/xmzJava/p/8578399.html 日常开发中,mybatis如果报错了调错起来会比较麻烦,因为一层套着一层,如果没有对myba ...

  7. mysql中主键和唯一键的区别

    区别项 primary key(主键) unique(唯一键约束) 唯一性 可以 可以 是否可以为空 不可以 可以 允许个数 只能有1个 允许多个 是否允许多列组合 允许 允许

  8. 权限控制和OAuth

    目录 1 权限控制是什么 1.1 ACL 1.2 RBAC 1.2.1 名词术语 1.2.2 RBAC定义 1.2.3 RBAC分类 1.2.3.1 RBAC0 1.2.3.2 RBAC1 1.2.3 ...

  9. flask中接收post传递数组方法

    list = request.form.getlist("表单名")

  10. JavaScript中常见的十五种设计模式

    在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于“类”. 在JavaScript中并没有类这种概念,JS中的函数属于一等对象,在JS中定义一个对象非常简单(var obj = {}), ...