Android TextView加载带有多张图片的HTML,并且解决图片造成的OOM
来源:互联网 发布:linux assert函数 编辑:程序博客网 时间:2024/06/07 09:50
版权声明:本文为博主原创文章,未经博主允许不得转载。
转载请注明本文出自 renxhui 的博客(http://blog.csdn.net/qq_34760508/article/details/70146189),请尊重他人的辛勤劳动成果,谢谢!
相信大家都有过需求需要加载从后台返回的部分HTML代码到我们的Android手机上需求,现有的android 原生控件有WebView 和 TextView 可以去加载HTML,由于现在的需求我们需要记载一段HTML代码在RecyclerView中显示去报,一开始我的思路直接就是直接在Item中镶嵌WebView,这个办法简单粗暴,我试了一下,结果发现不可以因为由于现在RecyclerView在滚动的时候不断地去刷新View ,导致如果在HTML有图片的话,也会不停的加载,这样是不行的,后来就转战TextView了 ,下面我们分析一下TextView。
首先我们要了解TextView加载HTML基本的方法
mTextView.setMovementMethod(ScrollingMovementMethod.getInstance());// 滚动
mTextView.setMovementMethod(LinkMovementMethod.getInstance());//设置超链接可以打开网页
Html.ImageGetter imageGetter = new Html.ImageGetter() {
public Drawable getDrawable(String source) { Drawable drawable = null; URL url; try { url = new URL(source); //Android 4.4 以后这步需要放在子线程中去操作 drawable = Drawable.createFromStream(url.openStream(), ""); // 获取网路图片 } catch (Exception e) { e.printStackTrace(); return null; } //这个后面我们要用到,可以控制显示在Textview上的图片大小 drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); return drawable; } };//这个第二个参数imageGetter是加载图片使用的,第三个参数是过滤标签使用的,我们后面要用到,做点击图片的处理 mTextView.setText(Html.fromHtml(htmlforImage, imageGetter, null));上面就是在网上可以百度到的基本操作,下面我们去做其他处理,其实就是加载图片的处理,比如图片的缩放和缓存**重点内容**我相信大部分同学在加载的话都会用webview去直接加载到一个Actiity中,这种的话webview就可以满足我们大本分的需求,可是现在我们需要把Html放在RecyclerView中去显示,webview就不在适用了,二用textview去加载就会出现各种问题,下面我们就去列出会出现的问题然后一一去解决他们
1, 在recyclerview滚动事会不断刷新Textview的内容,这个如果textview中只有文本的话当然不是问题,不过现在我们还包括图片,当然不能重复刷新,浪费流量,而且加载还特别慢,所以我们把加载过一次的图片缓存到本地就可以供下次加载使用了,不多少了,下面上代码
/**
* Created by jh on 2016/10/10.
*/
public class TextToHtmlUtils {
private Context mContext;private Dialog mDialog;private int mPosition;private static TextToHtmlUtils mInstance;private static HashMap<String, Bitmap> sPostDetialHashMap;private static HashMap<String, HashMap<String, Bitmap>> sHashMapHashMap;public static String TEAM_NEWS = "team_news";public static String POST_DETIAL = "post_detial";private static HashMap<String, Bitmap> sTeamNewsHashMap;public TextToHtmlUtils() { EventBus.getDefault().register(this);}public static TextToHtmlUtils getInstance() { if (mInstance == null) { synchronized (OkHttpManager.class) { if (mInstance == null) { mInstance = new TextToHtmlUtils(); //用map来存储图片,为之后回收图片做准备 sHashMapHashMap = new HashMap<>(); sPostDetialHashMap = new HashMap<>(); sTeamNewsHashMap = new HashMap<>(); } } } return mInstance;}public void getBitmap(final ToHtml compressBitmap, final String url, final Context context, final TextView textView, int position, final String type) { this.mPosition = position; this.mContext = context; //利用Rxjava去处理线程,把耗时线程放在子线程中 Observable.create(new Observable.OnSubscribe<CharSequence>() { @Override public void call(Subscriber<? super CharSequence> subscriber) { //这里去加载图片 // CharSequence test = Html.fromHtml(url); textView.setText("loading..."); CharSequence getbitmap = getbitmap(url, type); subscriber.onNext(getbitmap); subscriber.onCompleted(); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<CharSequence>() { @Override public void onNext(CharSequence bitmap) { compressBitmap.toFile(bitmap); } @Override public void onCompleted() { } @Override public void onError(Throwable e) { } });}private CharSequence getbitmap(String url, final String type) { Html.ImageGetter imageGetter = new Html.ImageGetter() { @Override public Drawable getDrawable(String source) { URL url; Drawable drawable = null; try { String filename = checkUrl(source); String saveFilePaht = Constants.IMAGE + filename; File file = new File(saveFilePaht); //判断图片是否已经存储 if (!file.exists()) { Response response = OkHttpManager.getInstanceSimaple(mContext).getAsyn1(source); byte[] bytes = response.body().bytes(); //利用BitmapFactory去显小图片避免oom,关于BitmapFactory不会用的自行百度 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; //BitmapFactory.decodeStream(inputStream, null, opts); BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts); //根据你想要的图片的大小去自动计算缩小倍数 int i = computeSampleSize(opts, -1, 1000 * 1000); opts.inSampleSize = i; opts.inJustDecodeBounds = false; opts.inInputShareable = true; opts.inPurgeable = true; opts.inPreferredConfig = Bitmap.Config.ARGB_4444; //把byte转换为bitmap Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts); BitmapUtils.compressAndSaveBitmapToSDCard(bitmap, filename, 80); drawable = BitmapUtils.bitmaptodrawable(bitmap); //吧bitmap存到map中 if (type.equals(POST_DETIAL)) { sPostDetialHashMap.put(filename, bitmap); sHashMapHashMap.put(type, sPostDetialHashMap); } else if (type.equals(TEAM_NEWS)) { sTeamNewsHashMap.put(filename, bitmap); sHashMapHashMap.put(type, sTeamNewsHashMap); } Logger.d("mmmmHtml","不存在"); } else { boolean recycle = isRecycle(filename, type); //图片已经存在,去内存中或者本地去加载 if (recycle) { //内存中不存在,本地加载 Bitmap bitmap = BitmapUtils.getBitmapInputStreamFromSDCard(filename); drawable = BitmapUtils.bitmaptodrawable(bitmap); if (type.equals(POST_DETIAL)) { sPostDetialHashMap.put(filename, bitmap); sHashMapHashMap.put(type, sPostDetialHashMap); } else if (type.equals(TEAM_NEWS)) { sTeamNewsHashMap.put(filename, bitmap); sHashMapHashMap.put(type, sTeamNewsHashMap); } Logger.d("mmmmHtml","SD卡"); } else { //内存加载 HashMap<String, Bitmap> stringBitmapHashMap = sHashMapHashMap.get(type); Bitmap bitmap = stringBitmapHashMap.get(filename); drawable = BitmapUtils.bitmaptodrawable(bitmap); Logger.d("mmmmHtml","Map"); } } //drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); //这个方法去计算你想要的图片的大小 matchDrawable(drawable); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return drawable; } }; CharSequence test = Html.fromHtml(url, imageGetter, new MyTagHandler()); return test;}private boolean isRecycle(String filename, String type) { if (sHashMapHashMap.containsKey(type)) { HashMap<String, Bitmap> stringBitmapHashMap = sHashMapHashMap.get(type); if (stringBitmapHashMap.containsKey(filename)) { Bitmap bitmap1 = stringBitmapHashMap.get(filename); boolean recycled = bitmap1.isRecycled(); return recycled; } } return true;}private void matchDrawable(Drawable drawable) { if (drawable.getIntrinsicHeight() < 300 && drawable.getIntrinsicWidth() < 500 && drawable.getIntrinsicHeight() > 10 && drawable.getIntrinsicWidth() > 10) { float i = 800 / drawable.getIntrinsicWidth(); float j = 400 / drawable.getIntrinsicHeight(); if (i <= j) { int width = (int) (drawable.getIntrinsicWidth() * j); int height = (int) (drawable.getIntrinsicHeight() * j); drawable.setBounds(0, 0, width, height); } else { int width = (int) (drawable.getIntrinsicWidth() * i); int height = (int) (drawable.getIntrinsicHeight() * i); drawable.setBounds(0, 0, width, height); } } else { drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); }}private String checkUrl(String source) { String[] split = source.split("/"); for (String sp : split) { if (sp.equals("attachments")) { String s = split[split.length - 1]; return s; } } String s = split[split.length - 3]; return s;}public static abstract class ToHtml { public abstract void toFile(CharSequence bitmap);}/** * 用来通知当解析器遇到无法识别的标签时该作出何种处理 */class MyTagHandler implements Html.TagHandler { /** * 参数: * opening:为true时表示某个标签开始解析,为false时表示该标签解析完 * tag:当前解析的标签 * output:文本中的内容 * xmlReader:xml解析器 */ @Override public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { if (tag.toLowerCase().equals("img")) {//解析<img/>标签(注意标签格式不是<img></img>) // Logger.e("opening-->", opening + ""); int len = output.length(); ImageSpan[] images = output.getSpans(len - 1, len, ImageSpan.class); String imgURL = images[0].getSource(); //添加点击事件 output.setSpan(new ImageClickSpan(mContext, imgURL), len - 1, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (tag.equalsIgnoreCase("strike")) {//自定义解析<strike></strike>标签 int len = output.length(); Logger.e("opening-->", opening + ""); if (opening) {//开始解析该标签,打一个标记 output.setSpan(new StrikethroughSpan(), len, len, Spannable.SPAN_MARK_MARK); } else {//解析结束,读出所有标记,取最后一个标记为当前解析的标签的标记(因为解析方式是便读便解析) StrikethroughSpan[] spans = output.getSpans(0, len, StrikethroughSpan.class); if (spans.length > 0) { for (int i = spans.length - 1; i >= 0; i--) { if (output.getSpanFlags(spans[i]) == Spannable.SPAN_MARK_MARK) { int start = output.getSpanStart(spans[i]); output.removeSpan(spans[i]); if (start != len) { output.setSpan(new StrikethroughSpan(), start, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } break; } } } } } else {//其他标签不再处理 } }}class ImageClickSpan extends ClickableSpan { private Context context; private String url; public ImageClickSpan(Context context, String url) { this.context = context; this.url = url; } @Override public void onClick(View widget) { showPicDialog(url); }}private void showPicDialog(String url) { String filename = checkUrl(url); String saveFilePaht = Constants.IMAGE + filename; Intent intent = new Intent(mContext, BigImageActivity.class); intent.putExtra("path", saveFilePaht); mContext.startActivity(intent);}public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels); int roundedSize; if (initialSize <= 8) { roundedSize = 1; while (roundedSize < initialSize) { roundedSize <<= 1; } } else { roundedSize = (initialSize + 7) / 8 * 8; } return roundedSize;}public static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) { double w = options.outWidth; double h = options.outHeight; int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength)); if (upperBound < lowerBound) { return lowerBound; } if ((maxNumOfPixels == -1) && (minSideLength == -1)) { return 1; } else if (minSideLength == -1) { return lowerBound; } else { return upperBound; }//回收bitmappublic void clearBitmap(String type) { for (String type1 : sHashMapHashMap.keySet()) { if (type1.equals(type)) { HashMap<String, Bitmap> stringBitmapHashMap = sHashMapHashMap.get(type1); for (String path : stringBitmapHashMap.keySet()) { Bitmap bitmap = stringBitmapHashMap.get(path); if (!bitmap.isRecycled() && bitmap != null) { bitmap.recycle(); Logger.d("mmmrecyBitmapeventbus", bitmap.toString() + type1); } } } }}
}
}
经过上面一系列的操作就可以在recyclerview中加载了
在activity调用如下
TextToHtmlUtils instance = getInstance();
instance.getBitmap(new TextToHtmlUtils.ToHtml() {
@Override
public void toFile(CharSequence html) {
holder.mTextView.setText(html);
}
}, Html, context, holder.mtextview, position,TextToHtmlUtils.POST_DETIAL);
最有谢谢大家观看,有什么更好的建议请留言给我
源码已经上传github
网址https://github.com/renxh4/TextViewForHtml
喜欢的话记得点一下关注哦
- Android TextView加载带有多张图片的HTML,并且解决图片造成的OOM
- android图片的三级缓存原理demo,解决图片加载OOM的问题,能加载上万张图片
- Android之解决太大太多图片造成的oom
- Android之解决太大太多图片造成的oom
- Android之解决太大太多图片造成的oom
- Android加载超大图片并且不会OOM的策略
- Android 解决加载图片过多出现oom--强大的Fresco
- Android实现图片的加载与释放(解决OOM问题)
- TextView加载Html并且显示图片
- 解决加载图片出现OOM的方法
- 使用LruCache缓存,轻松解决图片过多造成的OOM
- listView加载几百张图片不出现OOM的方法
- android 加载图片防止oom的处理
- Android加载图片,避免OOM的解决方案
- TextView加载带图片的Html解决方案
- 解决加载图片OOM
- 解决Android解析图片的OOM问题!!!
- 解决Android解析图片的OOM问题!!!
- Python与人工神经网络(10)——神经网络可以干什么
- Dijkstra队列优化矩阵版
- Linux学习--gdb调试core文件
- 范型擦除中的原始类型类型接口--个人见解记录,非官方
- Find Bottom Left Tree Value
- Android TextView加载带有多张图片的HTML,并且解决图片造成的OOM
- 在linux下安装交叉编译链
- 工厂模式
- 欢迎使用CSDN-markdown编辑器
- ubuntu 安装软件的一些命令aptitude,apt-get
- org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start: org.apache.
- 实现哈夫曼树
- Redis消息通知系统的实现
- 关于matplotlib的twinx()的使用