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 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!