如何從網路下載圖片

如何從網路下載圖片

情境

如果你想從網路上下載圖片, 該怎麼寫呢?
以下可能需要參考的觀念:

  • 如何使用Thread和Handler
  • 一些Http request的觀念。

程式碼說明

首先在XML先定義一個ImageView跟ProgressBar

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
    <RelativeLayout
        android:id="@+id/img_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <ProgressBar
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:id="@+id/progress_bar" />
        <ImageView
            android:visibility="gone"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:id="@+id/imageView"/>
    </RelativeLayout>
    <Button
        android:layout_below="@id/img_layout"
        android:id="@+id/download_btn"
        android:text="download img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

這樣一開始就讓使用者知道目前正在讀取, 不至於以為程式當機將程式關閉。

接著宣告一個類別叫作DownloadWebPicture

public class DownloadWebPicture {}

然後在裡面加入下面的程式碼

public class DownloadWebPicture {
    private Bitmap bmp;
    public Bitmap getImg(){
        return bmp;
    }
    public void handleWebPic(final String url, final Handler handler){
        new Thread(new Runnable(){
            @Override
            public void run() {
                bmp = getUrlPic(url);
                Message msg = new Message();
                msg.what = 1;
                handler.sendMessage(msg);
            }
        }).start();
    }
    public synchronized Bitmap getUrlPic(String url) {

        Bitmap webImg = null;

        try {
            URL imgUrl = new URL(url);
            HttpURLConnection httpURLConnection
                    = (HttpURLConnection) imgUrl.openConnection();
            httpURLConnection.connect();
            InputStream inputStream = httpURLConnection.getInputStream();
            int length = (int) httpURLConnection.getContentLength();
            int tmpLength = 512;
            int readLen = 0,desPos = 0;
            byte[] img = new byte[length];
            byte[] tmp = new byte[tmpLength];
            if (length != -1) {
                while ((readLen = inputStream.read(tmp)) > 0) {
                    System.arraycopy(tmp, 0, img, desPos, readLen);
                    desPos += readLen;
                }
                webImg = BitmapFactory.decodeByteArray(img, 0, img.length);
                if(desPos != length){
                    throw new IOException("Only read" + desPos +"bytes");
                }
            }
            httpURLConnection.disconnect();
        }
        catch (IOException e) {
            Log.e("IOException", e.toString());
        }
        return webImg;
    }
}

這邊如果你沒有Http以及IOStream的概念, 可以把這個方法當成一個黑盒子,
只需要傳入圖片網址, 就會回傳一個Bitmap的物件。
這個方法大致解釋一下, 網址用URL打開一個連線, 然後從這個連線取得一個串流,
之後先取得圖片的大小, 之後用一個陣列來裝圖片的串流,
再利用BitmapFactory的方法轉成Bitmap的物件。

接著在宣告一個方法handleWebPic

public void handleWebPic(final String url, final Handler handler){
    new Thread(new Runnable(){
        @Override
        public void run() {
            bmp = getUrlPic(url);
            Message msg = new Message();
            msg.what = 1;
            handler.sendMessage(msg);
        } 
    }).start();
}

這個方法傳入圖片位址, 以及一個Handler,
Handler是用來跑Thread的時候, 送出一個訊息給Main Thread知道我下載完成。

然後寫一個方法可以取得下載完成的圖片

public Bitmap getImg(){
    return bmp;
}

接下來到Activity這邊

private DownloadWebPicture loadPic;
private Handler mHandler;
private ProgressBar progressBar;
private ImageView imageView;
private final static String url = "https://dl.dropboxusercontent.com/u/24682760/Android_AS/DownloadImageDemo/monkey.png"; 

定義一些變數, 並且在onCreate內初始化這些變數

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    progressBar = (ProgressBar)findViewById(R.id.progress_bar);
    imageView = (ImageView)findViewById(R.id.imageView);
    loadPic = new DownloadWebPicture();

    mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what){
                case 1:
                 progressBar.setVisibility(View.GONE);
                 imageView.setImageBitmap(loadPic.getImg());
                 break;
            }
            super.handleMessage(msg);
        }
    };
    loadPic.handleWebPic(url, mHandler);
}

下載這張圖

從程式中可以看到, 建立一個Handler來處理Thread丟回來的變數,
當Thread通知Handler說處理好的時候, 我們就將ProgressBar隱藏起來,
然後將下載完的圖片丟進ImageView就完成了這支程式。

按下button


開始loading


讀取完畢就顯示圖片


記得加權限

<uses-permission android:name="android.permission.INTERNET" />

程式碼