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(),以及取出圖片的時候壓縮取縮略圖即可滿足我們的要求。

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