RxJava+Volley实现图片可点击的TextView
来源:互联网 发布:windows vista 真漂亮 编辑:程序博客网 时间:2024/05/17 09:43
前言
RxJava是一个实现java响应式编程的库。个人觉得它的优势是能够简化异步处理的逻辑,从而使整个业务流程能够更好的被读者理解,在处理异步业务的时候有处理同步业务的类似体验。个人理解可能不是很准确,对RxJava理解较浅的可以看看扔物线大牛的牛文给 Android 开发者的 RxJava 详解。
Volley这个库作为Android开发者肯定是知道的,是Google开源的一个网络框架,适用于数据小网络请求频繁的场景。
阅读本文前你应该先了解过RxJava和Volley。
实现思路
我这里需要解决的问题是使用TextView
加载一段HTML,HTML中有部分是图片,需要能够点击进入一个可缩放图片的页面查看图片的细节。
用TextView
加载HTML很简单textView.setText(Html.fromHtml(htmlString))
即可。加载HTML中的图片也很简单,只要实现一个ImageGetter
,然后在Html.fromHtml(htmlString, imageGetter, tagHandler)
使用即可。而实现TextView
中部分内容的点击使用ClickableSapn
可以实现。
解决问题的难点是,HTML中的图片在加载HTML的时候需要先从网络下载,我们需要在所有图片下载完成后再去刷新TextView
,达到显示图片的目的。之前使用Volley的时候都是使用CallBack<T>
的方式,这样在处理单个异步任务的时候还没有问题,但是在处理一组任务的时候就有些不足。之前RxJava就是一个很火很先进的库,自己也进行过了解,知道使用RxJava在处理一组异步任务的时候相当方便,所以就想尝试一下能否使用RxJava和Volley来解决遇到的问题。功夫不负有心人,在查过不少资料之后,经过尝试终于找到了一个可行的方案。
Volley除了提供Response.Listener<T>
,和Response.ErrorListener
的CallBack<T>
方式,还提供了RequestFuture<T>
的方式。这样我们就可以实现同步方式处理异步的请求。
举个栗子:
private RequestQueue requestQueue = Volley.newRequestQueue(context);// response listenerpublic void getImageFromNet(String url, int maxWidth, int maxHeight, Bitmap.Config config, Response.Listener<Bitmap> listener, Response.ErrorListener errorListener) { ImageRequest request = new ImageRequest(url, listener, maxWidth, maxHeight, config, errorListener); request.setShouldCache(true); request.setTag(url); requestQueue.add(request);}// request futurepublic Bitmap getImageFromNet(String url, int maxWidth, int maxHeight, Bitmap.Config config) throws ExecutionException, InterruptedException { RequestFuture<Bitmap> requestFuture = RequestFuture.newFuture(); ImageRequest request = new ImageRequest(url, requestFuture, maxWidth, maxHeight, config, requestFuture); request.setShouldCache(true); request.setTag(url); requestQueue.add(request); return requestFuture.get();}
设计思路:
- 因为使用了图片的缓存,所以在textView.setText(spanned)
中先显示已经缓存过的部分图片。
- 收集需要下载的图片地址之后,使用RxJava和Volley下载所有图片。
- 再次textView.setText(spanned)
,刷新TextView。
主要代码:
private OnImageClickListener listener;private CompositeSubscription compositeSubscription;public void setHtmlString(String htmlString) { // 要响应点击,这句必须 setMovementMethod(LinkMovementMethod.getInstance()); this.htmlString = htmlString; Spanned spanned = Html.fromHtml(htmlString, VolleyImageGetter.from(this), null); // 先显示文字和部分图片 setText(spanned); // 获取需要下载的图片url SpannableStringBuilder spannableStringBuilder; if (spanned instanceof SpannableStringBuilder) { spannableStringBuilder = (SpannableStringBuilder) spanned; } else { spannableStringBuilder = new SpannableStringBuilder(spanned); } ImageSpan[] imageSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ImageSpan.class); if (imageSpans != null && imageSpans.length > 0) { List<String> sources = new ArrayList<>(); for (ImageSpan span : imageSpans) { final String url = span.getSource(); if (!AndroidHelper.getImageDiskCache().hasBitMap(VolleyHelper.getCacheKey(url))) { LogHelper.e("add url:" + url); sources.add(url); } } if (sources.size() > 0) { getImageInRxWay(sources); } else { // 如果不需要下载图片则处理图片的点击 resetTextAndClickable(); } }}private void resetTextAndClickable() { Spanned spanned = Html.fromHtml(htmlString, VolleyImageGetter.from(ImageClickableTextView.this), null); SpannableStringBuilder spannableStringBuilder; if (spanned instanceof SpannableStringBuilder) { spannableStringBuilder = (SpannableStringBuilder) spanned; } else { spannableStringBuilder = new SpannableStringBuilder(spanned); } ImageSpan[] imageSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), ImageSpan.class); if (imageSpans != null && imageSpans.length > 0) { for (ImageSpan span : imageSpans) { final String url = span.getSource(); int start = spannableStringBuilder.getSpanStart(span); int end = spannableStringBuilder.getSpanEnd(span); ClickableSpan clickableSpan = new ClickableSpan() { @Override public void onClick(View widget) { if (listener != null) { listener.imageClicked(url); } } }; // remove other clickable span ClickableSpan[] clickableSpans = spannableStringBuilder.getSpans(start, end, ClickableSpan.class); if (clickableSpans != null && clickableSpans.length > 0) { for (ClickableSpan span1 : clickableSpans) { spannableStringBuilder.removeSpan(span1); } } // add image clickable span spannableStringBuilder.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } setText(spanned);}// get image from net in rxjava wayprivate void getImageInRxWay(List<String> sources) { if (compositeSubscription == null) { compositeSubscription = new CompositeSubscription(); } Subscription subscription = Observable.from(sources) .flatMap(new Func1<String, Observable<BitmapEntry>>() { @Override public Observable<BitmapEntry> call(String s) { return getBitmapEntry(s); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<BitmapEntry>() { @Override public void onCompleted() { LogHelper.e("onCompleted"); resetTextAndClickable(); } @Override public void onError(Throwable e) { if (e instanceof VolleyError) { VolleyError cause = (VolleyError) e.getCause(); String s = new String(cause.networkResponse.data, Charset.forName("UTF-8")); LogHelper.e(s); } else { LogHelper.e(e.getMessage()); } } @Override public void onNext(BitmapEntry entry) { // add bitmap to cache if (entry.getBitmap() != null) { AndroidHelper.getImageDiskCache().putBitmap(entry.getKey(), entry.getBitmap()); } } }); compositeSubscription.add(subscription);}// 使用defer来提高性能// defer操作符是直到有订阅者订阅时,才通过Observable的工厂方法创建Observable并执行,// 在使用rxjava中我们应该使用defer来包装慢的操作private Observable<BitmapEntry> getBitmapEntry(final String url) { return Observable.defer(new Func0<Observable<BitmapEntry>>() { @Override public Observable<BitmapEntry> call() { try { return Observable.just(getImageBitmap(url)); } catch (ExecutionException | InterruptedException e) { LogHelper.e(e.getMessage()); return Observable.error(e); } } });}private BitmapEntry getImageBitmap(String url) throws ExecutionException, InterruptedException { RequestFuture<Bitmap> future = RequestFuture.newFuture(); ImageRequest request = new ImageRequest(url, future, 0, 0, Bitmap.Config.RGB_565, future); VolleyHelper.addRequest2Queue(request); String key = VolleyHelper.getCacheKey(url); Bitmap bitmap = future.get(); return new BitmapEntry(key, bitmap);}public interface OnImageClickListener { public void imageClicked(String imageUrl);}
完整的代码:https://github.com/Zhaoyy/ikanxue/blob/2.x/app/src/main/java/com/mislead/ikanxue/app/view/ImageClickableTextView.java
个人对于RxJava的理解也是比较浅,相信多看多练也能掌握这个新思想。
参考
- http://stackoverflow.com/questions/32701331/rxjava-and-volley-requests
- https://github.com/zzhoujay/RichText/blob/master/app/src/main/java/zhou/widget/RichText.java
- RxJava+Volley实现图片可点击的TextView
- 实现可点击展开的 TextView
- 实现可点击展开的 TextView
- 实现可点击展开的 TextView
- TextView展示Html内可点击图片
- 实现TextView中的短电话号码可点击
- [Android]自定义可点击的TextView
- 带动画的点击可展开TextView
- 使用textview显示html里面的图文混排效果,并且图片可点击索引到
- textview中点击效果实现,比如点击textview中实现图片和文字的颜色变化(类似于button)
- 可控制文字、图片间距的TextView
- TextView技巧,例如:实现部分文字变色和可点击
- 基础控件——TextView实现部分文字可点击
- TextView加载HTML数据(网络图片以及图片点击事件的实现)
- UIScrollView+UIPageControl+NSTimer实现图片的自动滑动以及用户可手动切换,UIPageControl可点击
- UIScrollView+UIPageControl+NSTimer实现图片的自动滑动以及用户可手动切换,UIPageControl可点击
- UIScrollView+UIPageControl+NSTimer实现图片的自动滑动以及用户可手动切换,UIPageControl可点击
- UIScrollView+UIPageControl+NSTimer实现图片的自动滑动以及用户可手动切换,UIPageControl可点击
- 【FastDev4Android框架开发】Android首页图片自动无限循环轮播Gallery+FlowIndicator(二)
- java里程碑之泛型--泛型注意的几点
- 关于编辑器
- 黑马程序员——java学习日记十
- python基础
- RxJava+Volley实现图片可点击的TextView
- 多列式布局(报纸)
- dom doucument bom window解释!
- linearlayout 设置layout_marginRight居右不能生效的解决方法
- linux下iptables的编译及简单使用
- C语言的指针学习指导
- oracle中的字符替换
- HDU 5500 Reorder the Books(O(n)的复杂度就够了)——BestCoder Round #59(div.1 div.2)
- WebView基本使用