Android - 不用緩存能把圖片加載做成什麼樣子

本文記錄圖片加載功能中不涉及緩存的部分。

數據

先搞了一些數據,都是 pixabay 的免費圖。

public static final List<String> IMAGES = Arrays.asList(
    "https://cdn.pixabay.com/photo/2017/07/18/18/01/little-girl-2516578_960_720.jpg",
    "https://cdn.pixabay.com/photo/2016/04/23/10/43/child-1347385_960_720.jpg",
    "https://cdn.pixabay.com/photo/2015/06/02/20/58/little-girl-running-795505_960_720.jpg",
    "https://cdn.pixabay.com/photo/2014/10/07/01/38/girl-477015_960_720.jpg",
    "https://cdn.pixabay.com/photo/2015/07/27/18/52/water-863053_960_720.jpg",
    "https://cdn.pixabay.com/photo/2016/05/12/07/13/kids-1387118_960_720.jpg",
    "https://cdn.pixabay.com/photo/2017/06/22/23/40/picking-flowers-2432972_960_720.jpg",
    "https://cdn.pixabay.com/photo/2018/01/08/20/24/little-girl-3070211_960_720.jpg",
    "https://cdn.pixabay.com/photo/2015/06/25/17/56/baby-821627_960_720.jpg",
    "https://cdn.pixabay.com/photo/2017/07/18/18/02/chasing-butterflies-2516581_960_720.jpg",
    "https://cdn.pixabay.com/photo/2015/07/31/22/59/princess-869721_960_720.jpg"
);

加載的入口

在 RecyclerViewAdapter 的 bind 方法中啓動 AsyncTask。

@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
    imageView.setTag(url);
    ImageWorkTask task = new ImageWorkTask(url);
    task.execute();
    mTasks.add(task);
}

下載圖片

分爲兩個部分
一是網絡請求部分:

@Override
protected Bitmap doInBackground(String... strings) {
    HttpURLConnection connection = null;
    try {
        URL url = new URL(this.url);
        connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(false);
        connection.setDoInput(true);
        connection.setRequestMethod("GET");
        connection.setUseCaches(false);
        connection.setChunkedStreamingMode(0);
        connection.setConnectTimeout(3000);

        connection.connect();
        // 主要是爲了獲取 InputStream
        Bitmap bitmap = getBitmapFromInputStream(connection.getInputStream());
        return bitmap;
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }
    return null;
}

二是解析圖片部分:

private Bitmap getBitmapFromInputStream(InputStream input) {
    Bitmap bitmap = null;
    try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
        // 讀取所有數據,不直接 decodeStream,因爲可能圖片太大。
        byte[] buffer = new byte[1024];
        int len;
        while ((len = input.read(buffer)) >= 0) {
            output.write(buffer, 0, len);
        }
        byte[] array = output.toByteArray();
        // 計算縮放尺寸,這裏最好用 R.dimension 不要寫死。
        int scale = calculateScaleOfImage(array, 240 * 2, 240 * 2); 
        BitmapFactory.Options option = new BitmapFactory.Options();
        option.inSampleSize = scale;
        // 然後解析成 Bitmap
        bitmap = BitmapFactory.decodeByteArray(array, 0, array.length, option);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return bitmap;
}
private int calculateScaleOfImage(byte[] array, int maxWidth, int maxHeight) {
    BitmapFactory.Options option = new BitmapFactory.Options();
    option.inJustDecodeBounds = true;
    BitmapFactory.decodeByteArray(array, 0, array.length, option);
    int xScale = Math.round((float) option.outWidth / maxWidth);
    int yScale = Math.round((float) option.outHeight / maxHeight);
    return Math.max(xScale, yScale);
}

顯示出來

這裏用的是查找 tag 的策略,好處是不用保存 imageView 的引用。

@Override
protected void onPostExecute(Bitmap bitmap) {
    ImageView view = mRecyclerView.findViewWithTag(url);
    if (view != null) {
        view.setImageBitmap(bitmap);
    }
    mTasks.remove(url);
}

清理

所有的 ImageWorkTask 在退出界面後應取消掉。

public void clearTasks() {
    for (ImageWorkTask task : mTasks) {
        task.cancel(true);
    }
}

總結

由於 RecyclerView 複用 ItemView 的原因,滑動到被複用的 ItemView 並不會立刻顯示出正確的圖片,還是顯示原來的圖片,得等網絡下載完畢後才能顯示正確。不用緩存真的是體驗很差。

(ole)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章