android 自定义组件之URLImageView

fmms 10年前

最近想写一些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