android 自定義組件之URLImageView
最近想寫一些android小組件,因為最近各種事情也比較多,沒時間也沒精力再來自己單獨寫一個應用,寫組件也是為了寫應用更方便嘛^-^。這第 一個小組件是能夠顯示一個互聯網的圖片的ImageView,我把它叫做URLImageView,本來是覺得昨天一個晚上就可以搞定的,結果在寫的過程 中遇到了各種各樣的問題,這里就和大家分享一下。
URLImageView小組件的作用是顯示互聯網上的圖片。并在加載過程中顯示進度條。最終效果如下:
下載中(左):和下載完畢顯示(右)


這里我的實現方式是繼承自RelativeLayout。好了,廢話不說,上代碼。
package com.sheling.android.widget;
/**@author sheling
* 2012-4-2
*
* */
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
public class UrlImageView extends RelativeLayout {
private static final String LOG_TAG = "URLImageView";
private static String TEMP_STORGE_PATH_DIR = "/tmp/";
private Bitmap bitmap;
private String TEMP_STORGE_PATH_FILE ;
private URL url;
private Context context;
private ImageView imageView;
private ProgressBar progressBar;
public UrlImageView(Context context) {
super(context);
this.context = context;
init();
}
public UrlImageView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public UrlImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
init();
}
/**初始化布局信息*/
public void init(){
LayoutInflater.from(context).inflate(R.layout.url_image_view,this,true);;
imageView = (ImageView) findViewById(R.id.imageView);
progressBar = (ProgressBar) findViewById(R.id.processBar);
}
/**bind ImageUrl
* @throws MalformedURLException */
public void bindUrl(String urlString) throws MalformedURLException,IOException{
bindUrl(new URL(urlString));
}
/**bind ImageURL
* @throws IOException */
public void bindUrl(URL url) throws IOException{
Log.v(LOG_TAG, "bindURL...");
this.url = url;
String[] urlArr = url.toString().split("/");
TEMP_STORGE_PATH_FILE = TEMP_STORGE_PATH_DIR +urlArr[urlArr.length -1];
DownloadTask dTask = new DownloadTask();
dTask.execute(url);
}
class DownloadTask extends AsyncTask<URL, Long, String>{
@Override
protected String doInBackground(URL... params) {
// TODO get bitmap and set update process signal
/* 取得連接 */
HttpURLConnection conn;
File tmpFile = null;
try {
conn = (HttpURLConnection) url.openConnection();
conn.connect();
/* 取得返回的InputStream */
InputStream is = conn.getInputStream();
//得到網絡文件大小
long size = conn.getContentLength();
Log.v(LOG_TAG,"文件大小:"+size);
//下載存儲的文件路徑
tmpFile = new File(Environment.getExternalStorageDirectory() + TEMP_STORGE_PATH_FILE);
boolean needDownload = true;
if(tmpFile.exists()){
//若存在同名文件,【判斷大小再操作
FileInputStream fips = new FileInputStream(tmpFile);
long tmpSize = fips.available();
Log.v(LOG_TAG,"已存在文件大小:"+tmpSize);
fips.close();
Log.v(LOG_TAG, (tmpSize == size)+"");
Log.v(LOG_TAG, (Long.valueOf(tmpSize)==Long.valueOf(size))+"");
if(tmpSize == size){
Log.v(LOG_TAG, "already downloaded");
needDownload = false;
}else{
tmpFile.delete();
Log.e(LOG_TAG, "deleted the same file");
}
}
//需要下載再下載
if(needDownload){
tmpFile.createNewFile();
FileOutputStream fops = new FileOutputStream(tmpFile);
byte[] buffer = new byte[1024 * 8];
int length = 0;
int readed = 0;
while((length=is.read(buffer))!=-1){
fops.write(buffer);
fops.flush();
readed += length;
publishProgress((readed*100)/size);
Log.v(LOG_TAG,"readed");
}
/* 關閉InputStream */
fops.close();
is.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return tmpFile.toString();
}
@Override
protected void onProgressUpdate(Long... values) {
// TODO update ProgressBar
progressBar.setProgress(values[0].intValue());
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(String tmpFileStr) {
// TODO show bitmap
super.onPostExecute(tmpFileStr);
//讀取文件
File tmpFile = new File(tmpFileStr);
try {
bitmap = BitmapFactory.decodeStream(new FileInputStream(tmpFile));
Log.v(LOG_TAG, "bitmapPath:"+tmpFileStr);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//更新顯示
progressBar.setVisibility(View.GONE);
imageView.setVisibility(View.VISIBLE);
imageView.setImageBitmap(bitmap);
}
}
}
上述代碼在執行過程中會發現,每次運行都要重新去下載圖片。這段代碼并沒有按我們預期的目標執行(為了用戶考慮,能不要重復下載的資源就不用下)。然后去仔細對比一下兩個文件的大小,發現下載下來的文件總是要比原文件大。這問題出在哪呢。
計算一下,會發現我們下載下來的文件總是緩沖區的大小的倍數。說明這種下載文件的方式是錯誤的。這下問題便迎刃而解了。為了得到和原文件一樣大小的文件,就應該先用一定大小的緩沖區讀完之后,把剩下的不足一次緩沖區大小的一次讀完,且不加入其他新字節。
修改后doInBackground方法的代碼如下
@Override
protected String doInBackground(URL... params) {
// TODO get bitmap and set update process signal
/* 取得連接 */
HttpURLConnection conn;
File tmpFile = null;
try {
conn = (HttpURLConnection) url.openConnection();
conn.connect();
/* 取得返回的InputStream */
InputStream is = conn.getInputStream();
long size = conn.getContentLength();
Log.v(LOG_TAG,"文件大小:"+size);
tmpFile = new File(Environment.getExternalStorageDirectory() + TEMP_STORGE_PATH_FILE);
boolean needDownload = true;
//存在,判斷大小是否一致
if(tmpFile.exists()){
FileInputStream fips = new FileInputStream(tmpFile);
long tmpSize = fips.available();
Log.v(LOG_TAG,"已存在文件大小:"+tmpSize);
fips.close();
//一致,跳過下載
if(tmpSize == size){
Log.v(LOG_TAG, "already downloaded");
needDownload = false;
}else{
tmpFile.delete();
Log.e(LOG_TAG, "deleted the same file");
}
}
if(needDownload){
tmpFile.createNewFile();
FileOutputStream fops = new FileOutputStream(tmpFile);
int onceSize = 1024 * 4;
byte[] buffer = new byte[onceSize];
//計算固定大小的緩沖區要讀多少次
int readNum = (int) Math.floor(size/onceSize);
//得到剩余的字節長度
int leave = (int) (size - readNum * onceSize);
int length = 0;
int readed = 0;
for(int i=readNum;i>0;i--){
length=is.read(buffer);
fops.write(buffer);
fops.flush();
readed += length;
publishProgress((readed*100)/size);
Log.v(LOG_TAG,"readed");
}
buffer = new byte[leave];
length=is.read(buffer);
fops.write(buffer);
fops.flush();
readed += length;
publishProgress((readed*100)/size);
Log.v(LOG_TAG,"readed");
fops.close();
is.close();
}else{
//固定的顯示一個過程
publishProgress(80L);
}
} catch (IOException e) {
e.printStackTrace();
}
return tmpFile.toString();
}
這樣,一個簡單的讀取,下載,并顯示互聯網圖片的android小組件便完成了。當然,還可以給它美化,增加更多的功能。
附源碼:
http://code.google.com/p/sheling-android-urlimageview/downloads/list
本文由用戶 fmms 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!