Picasso解决 TextView加载html图片异步显示
项目中有这样一个需求:
textview加载一段 html标签 其中包含 "<Img url= " 图片异步展示 而且 根据图片的比例 宽度满屏展示。
思路:
重写textview Html.fromHtml方法 以及 图片Picasso展示(后面会附带Picasso 的两个转换类)
感觉网上没有合适的或者用的是Gilde加载 其实无论是Gilde还是Picasso加载豆豆都能满足我们的需求。
需求描述完毕 上张帅图:
好吧 废话不多说了 直接上实现代码
RichText:
public class RichText extends TextView { private Drawable placeHolder, errorImage;//占位图,错误图
private OnImageClickListener onImageClickListener;//图片点击回调
private HashSet<Target> targets;
private int d_w = 500;
private int d_h = 500; public RichText(Context context) {
this(context, null);
} public RichText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public RichText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); init(context, attrs);
} private void init(Context context, AttributeSet attrs) {
targets = new HashSet<>();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RichText);
placeHolder = typedArray.getDrawable(R.styleable.RichText_placeHolder);
errorImage = typedArray.getDrawable(R.styleable.RichText_errorImage); d_w = typedArray.getDimensionPixelSize(R.styleable.RichText_default_width, d_w);
d_h = typedArray.getDimensionPixelSize(R.styleable.RichText_default_height, d_h); if (placeHolder == null) {
placeHolder = new ColorDrawable(Color.GRAY);
}
placeHolder.setBounds(0, 0, d_w, d_h);
if (errorImage == null) {
errorImage = new ColorDrawable(Color.GRAY);
}
errorImage.setBounds(0, 0, d_w, d_h);
typedArray.recycle();
} /**
* 设置富文本
*
* @param text 富文本
*/
public void setRichText(String text) {
targets.clear();
Spanned spanned = Html.fromHtml(text, asyncImageGetter, null);
SpannableStringBuilder spannableStringBuilder;
if (spanned instanceof SpannableStringBuilder) {
spannableStringBuilder = (SpannableStringBuilder) spanned;
} else {
spannableStringBuilder = new SpannableStringBuilder(spanned);
} ImageSpan[] imageSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ImageSpan.class);
final List<String> imageUrls = new ArrayList<>(); for (int i = 0, size = imageSpans.length; i < size; i++) {
ImageSpan imageSpan = imageSpans[i];
String imageUrl = imageSpan.getSource();
int start = spannableStringBuilder.getSpanStart(imageSpan);
int end = spannableStringBuilder.getSpanEnd(imageSpan);
imageUrls.add(imageUrl); final int finalI = i;
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(View widget) {
if (onImageClickListener != null) {
onImageClickListener.imageClicked(imageUrls, finalI);
}
}
};
ClickableSpan[] clickableSpans = spannableStringBuilder.getSpans(start, end, ClickableSpan.class);
if (clickableSpans != null && clickableSpans.length != 0) {
for (ClickableSpan cs : clickableSpans) {
spannableStringBuilder.removeSpan(cs);
}
}
spannableStringBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
super.setText(spanned);
setMovementMethod(LinkMovementMethod.getInstance());
} private void addTarget(Target target) {
targets.add(target);
} /**
* 异步加载图片(依赖于Picasso)
*/
private Html.ImageGetter asyncImageGetter = new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
final URLDrawable urlDrawable = new URLDrawable();
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Drawable drawable = new BitmapDrawable(getContext().getResources(), bitmap);
drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
urlDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
urlDrawable.setDrawable(drawable);
RichText.this.setText(getText());
} @Override
public void onBitmapFailed(Drawable errorDrawable) {
// urlDrawable.setBounds(errorDrawable.getBounds());
urlDrawable.setDrawable(errorDrawable);
} @Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
// urlDrawable.setBounds(placeHolderDrawable.getBounds());
urlDrawable.setDrawable(placeHolderDrawable);
}
};
addTarget(target);
Picasso.with(getContext()).load(source).transform(new ImageTransform()).into(target);//.placeholder(placeHolder).error(errorImage)
return urlDrawable;
}
}; private static final class URLDrawable extends BitmapDrawable {
private Drawable drawable; @SuppressWarnings("deprecation")
public URLDrawable() {
} @Override
public void draw(Canvas canvas) {
if (drawable != null)
drawable.draw(canvas);
} public void setDrawable(Drawable drawable) {
this.drawable = drawable;
}
} public void setPlaceHolder(Drawable placeHolder) {
this.placeHolder = placeHolder;
this.placeHolder.setBounds(0, 0, d_w, d_h);
} public void setErrorImage(Drawable errorImage) {
this.errorImage = errorImage;
this.errorImage.setBounds(0, 0, d_w, d_h);
} public void setOnImageClickListener(OnImageClickListener onImageClickListener) {
this.onImageClickListener = onImageClickListener;
} public interface OnImageClickListener {
/**
* 图片被点击后的回调方法
*
* @param imageUrls 本篇富文本内容里的全部图片
* @param position 点击处图片在imageUrls中的位置
*/
void imageClicked(List<String> imageUrls, int position);
}
} // ============================ImageTransform 处理图片比例展示
public class ImageTransform implements Transformation { private String Key = "ImageTransform"; @Override
public Bitmap transform(Bitmap source) {//40 是我项目中 的图片间距
int targetWidth = ScreenUtil.getScreenWidth(App.getContext()) - DisplayUtil.dp2px(App.getContext(), 40);
if (source.getWidth() == 0) {
return source;
}
//如果图片小于设置的宽度,做处理
if (source.getWidth() < targetWidth) {
double aspectRatio = (double) source.getHeight() / (double) source.getWidth();
int targetHeight = (int) (targetWidth * aspectRatio); if (targetHeight != 0 && targetWidth != 0) {
Bitmap result = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
if (result != source) {
// Same bitmap is returned if sizes are the same
source.recycle();
}
return result;
} else {
return source;
}
} else {
return source;
}
} @Override
public String key() {
return Key;
}
} //=========================其中 40是我项目左右两边的间距
配置
<!--attrs 富文本-->
<declare-styleable name="RichText">
<attr name="placeHolder" format="reference" />
<attr name="errorImage" format="reference" />
<attr name="default_width" format="dimension" />
<attr name="default_height" format="dimension" />
</declare-styleable>
/**
* 获取屏幕的宽度px
*/
public static int getScreenWidth(Context context) {
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();// 创建了一张白纸
windowManager.getDefaultDisplay().getMetrics(outMetrics);// 给白纸设置宽高
return outMetrics.widthPixels;
}
public static int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
} //最后使用
<com.你的包名.RichText/> RichText.setRichText();就行 //=============================================================================扩展 Picasso加载圆形图片 解决比例失真问题 可不看
/**
* 毕加索 设置圆形头像
* Created by swplzj on 16/12/10.
*/ public class CircleTransform implements Transformation { private String Key = "CircleTransform"; private Context mContext; private int h = 60;
public CircleTransform(Context context) {
this.mContext = context;
} public CircleTransform(Context context,int height) {
this.mContext = context;
this.h = height;
} @Override
public Bitmap transform(Bitmap source) {// 60 是我图片头像的宽高度 压缩
Bitmap zoomBitmp = BitmapUtils.zoom(source, DisplayUtil.dp2px(mContext, h), DisplayUtil.dp2px(mContext, h));
Bitmap bitmap = BitmapUtils.circleBitmap(zoomBitmp);
source.recycle();
return bitmap;//返回圆形的Bitmap对象
} /**
* 该方法没有什么实际意义,但是要保证其返回的值不能为null!
* @return
*/
@Override
public String key() {
return Key;
}
}
public class BitmapUtils { /**将矩形的Bitmap对象转换为圆形的Bitmap
* @param source:待处理的 矩形的Bitmap
* @return :需返回的圆形的Bitmap
*/
public static Bitmap circleBitmap(Bitmap source){
//获取Bitmap的宽度
int width = source.getWidth();
//返回一个正方形的Bitmap对象
Bitmap bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
//提供指定宽高的canvas
Canvas canvas = new Canvas(bitmap);
//提供画笔
Paint paint = new Paint();
paint.setAntiAlias(true);
//背景:在画布上绘制一个圆
canvas.drawCircle(width / 2, width / 2, width / 2, paint); //设置图片相交情况下的处理方式
//setXfermode:设置当绘制的图像出现相交情况时候的处理方式的,它包含的常用模式有哪几种
//PorterDuff.Mode.SRC_IN 取两层图像交集部门,只显示上层图像,注意这里是指取相交叉的部分,然后显示上层图像
//PorterDuff.Mode.DST_IN 取两层图像交集部门,只显示下层图像
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//前景:在画布上绘制一个bitmap
canvas.drawBitmap(source, 0, 0, paint); return bitmap; } /**对bitmap进行压缩处理
* @param source :需要被处理的Bitmap
* @param width 需要压缩成的宽度 必须为浮点型
* @param height 需要压缩成的高度 必须为浮点型
* @return 返回压缩后的Bitmap
* 注意!必须提供参数2,3为浮点型。
*/
public static Bitmap zoom(Bitmap source,float width,float height){
Matrix matrix = new Matrix();
float scaleX = width / source.getWidth();
float scaleY = height / source.getHeight();
matrix.postScale(scaleX, scaleY); Bitmap bitmap = Bitmap.createBitmap(source,0,0,source.getWidth(),source.getHeight(),matrix,true);
return bitmap;
}
}
遗漏或者不清楚的可以联系我QQ群:521039620 Android&Go,Let's go!
感谢作者 https://github.com/zzhoujay/RichText (Gilde方式实现)
以及没提到的网上参考 谢谢大家。 ==================2017年4月26号 更新==========================
RichText 加载多张图片 或者图片超出屏幕处理
private Html.ImageGetter asyncImageGetter = new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
final URLDrawable urlDrawable = new URLDrawable();
Target target = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
int screenWidth = ScreenUtil.getScreenWidth(App.getContext());// 获取屏幕宽度
screenWidth = screenWidth- DisplayUtil.dp2px(App.getContext(),40);// 这个是我项目中 左右距离20dp
int height = bitmap.getHeight() * screenWidth / bitmap.getWidth(); Bitmap result = Bitmap.createScaledBitmap(bitmap, screenWidth, height, true);//等比压缩 设置 true 3M 压缩到200多K 关于清晰度 你回头可以自己调 优化
Drawable drawable = new BitmapDrawable(getContext().getResources(), result);
drawable.setBounds(0, 0, result.getWidth(), result.getHeight());
urlDrawable.setBounds(0, 0, result.getWidth(), result.getHeight()); urlDrawable.setDrawable(drawable);
RichText.this.setText(getText());
} @Override
public void onBitmapFailed(Drawable errorDrawable) {
urlDrawable.setDrawable(errorDrawable);
} @Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
urlDrawable.setDrawable(placeHolderDrawable);
}
};
addTarget(target);
Picasso.with(getContext()).load(source).transform(new ImageTransform()).into(target);//.placeholder(placeHolder).error(errorImage)
return urlDrawable;
}
};
==== ps 关于三星手机 只能成功加载一张图片 还在解决中ing
Picasso解决 TextView加载html图片异步显示的更多相关文章
- 解决iframe加载的内容有时显示有时不显示
在ASP.NET MVC项目中遇到了这样的一个问题,假设父页面有一个iframe <iframe id=" width="100%" height="10 ...
- WPF中加载高分辨率图片性能优化
在最近的项目中,遇到一个关于WPF中同时加载多张图片时,内存占用非常高的问题. 问题背景: 在一个ListView中同时加载多张图片,注意:我们需要加载的图片分辨率非常高. 代码: XAML: < ...
- Android开发技巧——TextView加载HTML的图片及代码显示问题
前几天在做一个Gradle用户指南的应用程序,使用的是TextView来加载HTML内容(至于为什么不用WebView,我也没有认真使用并比较过,也许以后会换吧),其中遇见了一些纠结的问题,所幸主要的 ...
- Android ListView 图片异步加载和图片内存缓存
开发Android应用经常需要处理图片的加载问题.因为图片一般都是存放在服务器端,需要联网去加载,而这又是一个比较耗时的过程,所以Android中都是通过开启一个异步线程去加载.为了增加用户体验,给用 ...
- 图片_ _Android有效解决加载大图片时内存溢出的问题 2
Android有效解决加载大图片时内存溢出的问题 博客分类: Android Android游戏虚拟机算法JNI 尽量不要使用setImageBitmap或 setImageResource或 Bit ...
- Adobe Edge Animate –解决图形边缘精确检测问题-通过jquery加载svg图片
Adobe Edge Animate –解决图形边缘精确检测问题-通过jquery加载svg图片 版权声明: 本文版权属于 北京联友天下科技发展有限公司. 转载的时候请注明版权和原文地址. 在edge ...
- Android开发中如何解决加载大图片时内存溢出的问题
Android开发中如何解决加载大图片时内存溢出的问题 在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给 ...
- Android开发问题积累 <加载在线Gif><WebView无法加载网页图片>
在线Gif加载 解决办法 Glide完美解决 Glide.with(context).load(pic).placeholder(R.drawable.loading).into(imageView) ...
- 我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)
正如在<我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)>一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piec ...
随机推荐
- Visual Studio 单元测试之四---Generic测试
原文:Visual Studio 单元测试之四---Generic测试 这里的Generic我觉得理解为外部测试更合适.因为在这种测试模式下Visual Studio只是启动一个外部的程序,然后通过返 ...
- QT Creater与libusb使用
新建一个C项目,然后修改.pro文件,添加LIBS一行 TEMPLATE = app CONFIG += console CONFIG -= app_bundle CONFIG -= qt LIBS ...
- openwrt固件支持3G和4G上网卡
http://wiki.openwrt.org/doc/howtobuild/wireless-router-with-a-3g-dongle Building image with support ...
- uva 11181 - Probability|Given
条件概率公式:P( A|B ) = P( AB ) / P( B ) 表示在事件B发生的前提,事件A发生的可能性: 问题的: 复位事件E:r个人买东西: 事件Ei:文章i个人买东西: 的要求是P( E ...
- 进击的Android注入术《二》
继续 在<一>里,我把基本思路描写叙述了一遍,接下为我们先从注入開始入手. 注入 分类 我们平时所说的代码注入,主要静态和动态两种方式 静态注入,针对是可运行文件,比方平时我们改动ELF, ...
- solr与.net主从复制
solr主从复制 solr与.net系列课程(七)solr主从复制 既然solr是解决大量数据全文索引的方案,由于高并发的问题,我们就要考虑solr的负载均衡了,solr提供非常简单的主从复制的 ...
- [译]Java垃圾回收器的类型
说明:这篇文章来翻译来自于Javapapers 的Types of Java Garbage Collectors 在这部分的教程中我们将讲到可使用的四种不同类型的Java垃圾回收器.垃圾回收是Jav ...
- 鼠标hover事件
JS: // ========== // = 鼠标hover事件 = // ========== function showHide (btn,box) { $(btn).hover(function ...
- wsgi-restful-routes具体解释:
感谢朋友支持本博客,欢迎共同探讨交流,因为能力和时间有限,错误之处在所难免,欢迎指正! 如有转载,请保留源作者博客信息. Better Me的博客:blog.csdn.net/tantexian 如需 ...
- Android项目--获取系统通讯录列表
----------------- 通讯录列表 ----------------- 按常理来说,获取系统通讯录列表,无非就是将通讯录的数据库打开获取数据,适配,添加即可. Cursor cursor; ...