Android加载网络图片

    |     2015年8月14日   |   网络通讯   |     0 条评论   |    2709

加载网络图片和加载字符串类似:走 Http 拿到 InputStream,再用 BitmapFactory.decodeStream。耗时必须放异步任务。列表多图时还要处理滚动错位:把 AsyncTask 绑到 ImageView 上,按 URL 决定取消还是复用。


一、从 URL 解码 Bitmap

URL url = new URL(httpUrl);
InputStream in = url.openStream();
Bitmap bitmap = BitmapFactory.decodeStream(in);

url.openStream() 等价于:

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
InputStream is = conn.getInputStream();

源码就是封装这两行:

/**
 * Equivalent to {@code openConnection().getInputStream(types)}.
 */
public final InputStream openStream() throws IOException {
    return openConnection().getInputStream();
}

二、单张图:AsyncTask + ProgressBar

public Bitmap downLoadBitmap(String httpUrl) {
    InputStream in = null;
    try {
        URL url = new URL(httpUrl);
        in = url.openStream();
        Bitmap bitmap = BitmapFactory.decodeStream(in);
        return bitmap;
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return null;
}
String httpUrl = "http://d.hiphotos.baidu.com/image/h%3D360/sign=ba3bf5f839c79f3d90e1e2368aa1cdbc/f636afc379310a55c01f6ef3b54543a9822610f9.jpg";

new AsyncTask<String, Void, Bitmap>() {
    protected void onPreExecute() {
        mProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        String httpUrl = params[0];
        return downLoadBitmap(httpUrl);
    }

    @Override
    protected void onPostExecute(Bitmap result) {
        mProgressBar.setVisibility(View.GONE);
        if (result != null)
            mShowImg.setImageBitmap(result);
    }
}.execute(httpUrl);

界面用 ImageView,ProgressBar 表示加载中:

imageloader1


三、多图:GridView 里直接开任务会错位

取图方式和单张一样,显示换成 GridView。一屏时看不出问题;图多、上下滚时 getView 反复启动 AsyncTask,就会图片错位。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final ImageView loaderImageView;
    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.item_my_gridview_layout, null);
        loaderImageView = (ImageView) convertView.findViewById(R.id.my_gridview_img);
        convertView.setTag(loaderImageView);
    } else {
        loaderImageView = (ImageView) convertView.getTag();
    }

    String itemUrl = (String) getItem(position);

    new AsyncTask<String, Void, Bitmap>() {
        @Override
        protected Bitmap doInBackground(String... params) {
            return downLoadBitmap(params[0]);
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            if (result != null)
                loaderImageView.setImageBitmap(result);
        }
    }.execute(itemUrl);

    return convertView;
}

效果如下:

imageloader2


四、按 URL 绑定 / 取消 AsyncTask

把任务引用存进专用 AsyncDrawable。新 URL 与正在跑的不同就 cancel;相同则不再开新任务。完成后用 WeakReference 确认 ImageView 仍在,且当前任务仍是它身上那个,才 setImageBitmap。

public void loadBitmap(Resources res, String imageUrl, ImageView imageView, int resId) {
    if (cancelPotentialWork(imageUrl, imageView)) {
        BitmapWorkerTask task = new BitmapWorkerTask(imageView);
        AsyncDrawable asyncDrawable = new AsyncDrawable(res,
                BitmapFactory.decodeResource(res, resId), task);
        imageView.setImageDrawable(asyncDrawable);
        task.execute(imageUrl);
    }
}

public static boolean cancelPotentialWork(String imageUrl, ImageView imageView) {
    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
    if (bitmapWorkerTask != null) {
        final String bitmapData = bitmapWorkerTask.imageUrl;
        if (!bitmapData.equals(imageUrl)) {
            bitmapWorkerTask.cancel(true);
        } else {
            return false;
        }
    }
    return true;
}

private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
    if (imageView != null) {
        final Drawable drawable = imageView.getDrawable();
        if (drawable instanceof AsyncDrawable) {
            final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
            return asyncDrawable.getBitmapWorkerTask();
        }
    }
    return null;
}

static class AsyncDrawable extends BitmapDrawable {
    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

    public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
        super(res, bitmap);
        bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
    }

    public BitmapWorkerTask getBitmapWorkerTask() {
        return (BitmapWorkerTask) bitmapWorkerTaskReference.get();
    }
}

class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
    private final WeakReference<ImageView> imageViewReference;
    private String imageUrl = "";

    public BitmapWorkerTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        imageUrl = params[0];
        return downLoadBitmap(imageUrl);
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = (ImageView) imageViewReference.get();
            final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
            if (this == bitmapWorkerTask && imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}
场景 做法
取流 URL.openStream() = openConnection().getInputStream()
解码 BitmapFactory.decodeStream
单张 AsyncTask + ProgressBar
列表复用 AsyncDrawable 存任务引用,URL 不同则 cancel

一句话总结:网络图 = 开流 decodeStream,必须异步;GridView 复用时要把 AsyncTask 绑在 ImageView 上,按 URL 取消旧任务,避免滚动错位。

转载请注明来源:Android加载网络图片
本文链接地址:https://ai.zhousir.top/?p=955

上一篇:

下一篇:

回复 取消