zoukankan      html  css  js  c++  java
  • 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

    文章来自 sheling 的博客园: http://www.cnblogs.com/sheling
    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,
      且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    我的独立博客 http://blog.iyestin.com
  • 相关阅读:
    函数
    向discuz里填充数据
    CI 基础
    FlashBuilder设置显示字符集
    win2003 Email邮件服务器搭配
    CI 模块化显示[仿照shopex功能]
    DW的鸟语
    CI 视图里加视图
    silverlight模拟单击事件
    自定义XML配置文件的操作类(转)
  • 原文地址:https://www.cnblogs.com/sheling/p/2430506.html
Copyright © 2011-2022 走看看