Android中类似微博论坛中用ImageGetter异Html图片

在像微博、论坛等,往往需要显示富文本信息,其中一个常用的方法可能就是Android本身支持的Html标签所持有的方法Html.fromHtml(String);通过这个方法来自动解析String中的Html标签,来达到显示富文本的目的。那么我们来了解下

1、Html.fromHtml(String ,ImageGetter, TagHandler ) 来解析带有Html标签的字符串,参数3设为null即可或者直接Html.fromHtml(String ) 
2、如果需要对超链接、邮箱、电话等设置点击响应,则需要添加 setMovementMethod(LinkMovementMethod.getInstance())

下面来具体说下,fromHtml()第二个参数为ImageGetter,参数的意义在于处理富文本中的图片标签,也就是用于接收图片并返回图片。那么图片是哪来的呢,当然是网上,也就是我们需要进行下载图片的操作,每张图片通过网络获得字节流来进行保存或者处理显示等等。这里主要来看一下ImageGetter的写法,我们来看代码

public class HtmlImageGetter implements Html.ImageGetter {

private static final String TAG = "IMAGE_GETTER";

Activity context;
TextView textView;

public HtmlImageGetter(Activity context, TextView tv) {
this.context = context;
this.textView = tv;
}

@Override
public Drawable getDrawable(String source) {

// 1、取缓存
if (BaseApplication.getInstance().mTitleBitmaps.containsKey(source)) {
SoftReference<Bitmap> cacheBitmap = BaseApplication.getInstance().mTitleBitmaps
.get(source);
Bitmap softBitmap = null;
if (cacheBitmap != null) {
softBitmap = cacheBitmap.get();
Drawable softDrawable = new BitmapDrawable(softBitmap);
softDrawable.setBounds(getDefaultBitmapRounds(context));

return softDrawable;
}
}
// 2、取文件
String fileName = source.substring(source.lastIndexOf("/") + 1);
File file = new File(Constants.TEAM_CACHE_IMAGE + fileName);
if (file.exists()) {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = false;
opts.inSampleSize = 2;

Bitmap fileBitmap = BitmapFactory.decodeFile(
Constants.TEAM_CACHE_IMAGE + fileName, opts);
Drawable fileDrawable = new BitmapDrawable(fileBitmap);
fileDrawable.setBounds(getDefaultBitmapRounds(context));

return fileDrawable;
}

// 3、取网络请求
URLDrawable uRLDrawable = new URLDrawable(context);
LoadImageAsync loadBitmap = new LoadImageAsync(uRLDrawable);
loadBitmap.execute(source, fileName);
return uRLDrawable;
}

/**
 * 图片保存到本地
 * 
 * @param in
 * @param dir
 * @param fileName
 * @throws IOException
 */
public void saveBitmap2File(InputStream in, String dirPath, String fileName)
throws IOException {

if (in == null) {
return;
}

if (dirPath == null || dirPath.equals("")) {
return;
}

if (fileName == null || fileName.equals("")) {
return;
}

if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
return;
}

File dir = new File(dirPath);
if (!dir.exists()) {
dir.mkdirs();
}

File file = new File(dirPath + fileName);
if (!file.exists()) {
file.createNewFile();
}

OutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int length = -1;

while ((length = in.read(buffer)) != -1) {
out.write(buffer, 0, length);
}

out.flush();
out.close();
}
       //网络下载图片
public class LoadImageAsync extends AsyncTask<String, Void, Drawable> {

URLDrawable mUrlDrawable;

public LoadImageAsync(URLDrawable mUrlDrawable) {
this.mUrlDrawable = mUrlDrawable;
}

@Override
protected Drawable doInBackground(String... arg0) {

String url = arg0[0];
String fileName = arg0[1];

URL Url;
Drawable drawable = null;
try {
Url = new URL(url);
InputStream in = Url.openStream();
// 保存到文件
saveBitmap2File(in, Constants.TEAM_CACHE_IMAGE, fileName);
in.close();

File file = new File(Constants.TEAM_CACHE_IMAGE + fileName);
if (file.exists()) {
Bitmap bitmap = BitmapManager.decodeSampledBitmapFromFile(
Constants.TEAM_CACHE_IMAGE + fileName, 480, 800);
// 保存到缓存
if (!BaseApplication.getInstance().mTitleBitmaps
.containsKey(url)) {
BaseApplication.getInstance().mTitleBitmaps.put(url,
new SoftReference<Bitmap>(bitmap));
}

drawable = new BitmapDrawable(bitmap);
drawable.setBounds(getDefaultBitmapRounds(context));
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return drawable;
}

@Override
protected void onPostExecute(Drawable result) {

if (result != null) {
mUrlDrawable.drawable = result;
HtmlImageGetter.this.textView.requestLayout();
}
}
}
       
public class URLDrawable extends BitmapDrawable {

protected Drawable drawable;

public URLDrawable(Context context) {
this.setBounds(getDefaultBitmapRounds(context));
drawable = context.getResources().getDrawable(
R.drawable.default_news);
drawable.setBounds(getDefaultBitmapRounds(context));
}

@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (drawable != null) {
drawable.draw(canvas);
}
}
}
        //图片显示大小
public Rect getDefaultBitmapRounds(Context context) {

return new Rect(0, 0,
ScreenManager.getScreenWidth((Activity) context) / 2,
ScreenManager.getScreenWidth((Activity) context) * 3 / 4);
}

}

以上是完整的ImageGetter的代码,那么这里来解释一下;
首先,整个代码分为3个类:
(1)主类 HtmlImageGetter需要继承Html.ImageGetter;这里需要重写一个方法getDrawable(String url),从字面上看就是得到Drawable图片,也就是我们要显示的图片,从这里我们明白了,得到图片的操作要写在getDrawable()中,方法的参数url,也就是资源的位置,即下载地址,具体来说就是Html标签中的<img>标签里的网络地址,下载也是基于这个地址的。
(2)子类LoadImageAsync,一看就知道这是一个异步加载图片的AsyncTask类,我们用他来实现异步加载。那么为什么异步加载呢?我们想象一下ListView中会有多少Item,如果每个Item都有一张Image,我们的程序也就崩溃的不能再崩溃了,所以异步加载是保证正常运行甚至用户体验必须的操作。
(3)子类URLDrawable ,这个类的作用就是我们在加载图片的时候可以设置一张默认显示的图片,然后加载完成的同时进行替换,这样有利于良好的用户体验。

        其次,看构造我们可以看到,传入了一个TextView,那么为什么呢?试想,如果单纯在getDrawable中开启线程的话,下载完成后我们如何将线程的Drawable作为返回值供getDrawable返回呢,答案很显然是否定的。我们的做法是先返回一个null,也就是显示默认图片,等下载完成后通过刷新布局来显示Html图片;所以textview的作用就是用来刷新布局。

       然后先来看AsyncTask部分,这个类主要实现下载网络图片的功能;里面的内容很简单:
首先1、下载图片,也就是打开连接得到字节流  2、保存图片到本地  3、从本地取出该图片,并保存到缓存(软引用序列) 4、处理返回
       首先下载图片,getDrawavle的参数是下载地址,将他传入AsyncTask,打开连接得到字节流,然后将流保存到本地。这里应该不同解释了。这里采用的过程是先保存到本地,再从本地取出图片,而不是直接将流解析成图片返回;因为后者需要两次打开流,浪费资源。从本地取出该图片,在处理之前需要先保存到软引用的缓存队列,这里我把这个Map存到Application的基层类中,避免数次重置。从本地读取的时候要注意我们只需要缩略图即可,节省内存也防止OOM.之后是图片的处理返回,注意的是一定要设置setBounds(),否则不显示; OnPOST中刷新一下布局来显示下载的图片

再来看getDrawable中的逻辑,首先读取缓存的图片,缓存是最快的,然后没有的话读取本地文件,最后是网络下载,一般都是这么处理,不多作解释了,还要注意设定显示大小setBounds(),以及取出图片的时候压缩取缩略图即可满足我们的要求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章