android 自定義組件之URLImageView

fmms 12年前發布 | 33K 次閱讀 Android Android開發 移動開發

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

URLImageView小組件的作用是顯示互聯網上的圖片。并在加載過程中顯示進度條。最終效果如下:

下載中(左):和下載完畢顯示(右)

android 自定義組件之URLImageView

android 自定義組件之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

文章來自 sheling 的博客園: http://www.cnblogs.com/sheling

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