android listview 非同步載入圖片並防止錯位

來源:互聯網
上載者:User

標籤:android   des   blog   http   使用   width   

網上找了一張圖, listview 非同步載入圖片之所以錯位的根本原因是重用了 convertView 且有非同步作業.

如果不重用 convertView 不會出現錯位現象, 重用 convertView 但沒有非同步作業也不會有問題。

我簡單分析一下:

當重用 convertView 時,最初一屏顯示 7 條記錄, getView 被調用 7 次,建立了 7 個 convertView.

當 Item1 划出螢幕, Item8 進入螢幕時,這時沒有為 Item8 建立新的 view 執行個體, Item8 複用的是

Item1 的 view 如果沒有非同步不會有任何問題,雖然 Item8 和 Item1 指向的是同一個 view,但滑到

Item8 時刷上了 Item8 的資料,這時 Item1 的資料和 Item8 是一樣的,因為它們指向的是同一塊記憶體,

但 Item1 已滾出了螢幕你看不見。當 Item1 再次可見時這塊 view 又涮上了 Item1 的資料。

 

但當有非同步下載時就有問題了,假設 Item1 的圖片下載的比較慢,Item8 的圖片下載的比較快,你滾上去

使 Item8 可見,這時 Item8 先顯示它自己下載的圖片沒錯,但等到 Item1 的圖片也下載完時你發現

Item8 的圖片也變成了 Item1 的圖片,因為它們複用的是同一個 view。 如果 Item1 的圖片下載的比

Item8 的圖片快, Item1 先刷上自己下載的圖片,這時你滑下去,Item8 的圖片還沒下載完, Item8

會先顯示 Item1 的圖片,因為它們是同一快記憶體,當 Item8 自己的圖片下載完後 Item8 的圖片又刷成

了自己的,你再滑上去使 Item1 可見, Item1 的圖片也會和 Item8 的圖片是一樣的,

因為它們指向的是同一塊記憶體。

 

最簡單的解決方案就是網上說的,給 ImageView 設定一個 tag, 並預設一個圖片。

當 Item1 比 Item8 圖片下載的快時, 你滾下去使 Item8 可見,這時 ImageView 的 tag 被設成了

Item8 的 URL, 當 Item1 下載完時,由於 Item1 不可見現在的 tag 是 Item8 的 URL,所以不滿足條件,

雖然下載下來了但不會設定到 ImageView 上, tag 標識的永遠是可見 view 中圖片的 URL。

關鍵代碼如下:

public View getView(int position, View convertView, ViewGroup parent) {    ViewHolder holder = null;    if (convertView == null) {        holder = new ViewHolder();        convertView = LayoutInflater.from(context).inflate(                R.layout.list_item, null);        holder.img = (ImageView) convertView.findViewById(R.id.userimage);        convertView.setTag(holder);    } else {        holder = (ViewHolder) convertView.getTag();    }    User user = list.get(position);    // 給 ImageView 設定一個 tag    holder.img.setTag(user.getImgUrl());    // 預設一個圖片    holder.img.setImageResource(R.drawable.ic_launcher);    final String tmpImageUrl = user.getImgUrl();    if (user.getImgUrl() != null && !user.getImgUrl().equals("")) {        Bitmap bitmap = imageLoader.loadImage(holder.img, user.getImgUrl(),                new ImageDownloadCallBack() {                    @Override                    public void onImageDownloaded(ImageView imageView,                            Bitmap bitmap) {                        // 通過 tag 來防止圖片錯位                        if (imageView.getTag() != null                                && imageView.getTag().equals(tmpImageUrl)) {                            imageView.setImageBitmap(bitmap);                        }                    }                });        if (bitmap != null) {            holder.img.setImageBitmap(bitmap);        }    }    return convertView;}

我參考網上資料寫了一個 listview 非同步載入圖片的 DEMO:

(1) 使用線程池

 沒有線程池,當圖片非常多,快速滑動  listview 時由於下載每個圖片都開了一個線程,

 可能出現 OOM (out of memory)。

(2) 記憶體、檔案雙緩衝

 這裡也使用 SoftReference 軟引用  

/** * 圖片非同步載入類 *  * @author Leslie.Fang * @company EnwaySoft *  */public class AsyncImageLoader {    // 最大線程數    private static final int MAX_THREAD_NUM = 10;    private Map<String, SoftReference<Bitmap>> imageCaches = null;    private FileUtil fileUtil;    // 線程池    private ExecutorService threadPools = null;    public AsyncImageLoader(Context context) {        imageCaches = new HashMap<String, SoftReference<Bitmap>>();        fileUtil = new FileUtil(context);    }    public Bitmap loadImage(final ImageView imageView, final String imageUrl,            final ImageDownloadCallBack imageDownloadCallBack) {        final String filename = imageUrl                .substring(imageUrl.lastIndexOf("/") + 1);        final String filepath = fileUtil.getAbsolutePath() + "/" + filename;        // 先從軟引用中找        if (imageCaches.containsKey(imageUrl)) {            SoftReference<Bitmap> reference = imageCaches.get(imageUrl);            Bitmap bitmap = reference.get();            // 軟引用中的 Bitmap 對象可能隨時被回收            // 如果軟引用中的 Bitmap 已被回收,則從檔案中找            if (bitmap != null) {                Log.i("aaaa", "cache exists " + filename);                return bitmap;            }        }        // 從檔案中找        if (fileUtil.isBitmapExists(filename)) {            Log.i("aaaa", "file exists " + filename);            Bitmap bitmap = BitmapFactory.decodeFile(filepath);            // 重新加入到記憶體軟引用中            imageCaches.put(imageUrl, new SoftReference<Bitmap>(bitmap));            return bitmap;        }        // 軟引用和檔案中都沒有再從網路下載        if (imageUrl != null && !imageUrl.equals("")) {            if (threadPools == null) {                threadPools = Executors.newFixedThreadPool(MAX_THREAD_NUM);            }            final Handler handler = new Handler() {                @Override                public void handleMessage(Message msg) {                    if (msg.what == 111 && imageDownloadCallBack != null) {                        Bitmap bitmap = (Bitmap) msg.obj;                        imageDownloadCallBack.onImageDownloaded(imageView,                                bitmap);                    }                }            };            Thread thread = new Thread() {                @Override                public void run() {                    Log.i("aaaa", Thread.currentThread().getName()                            + " is running");                    InputStream inputStream = HTTPService.getInstance()                            .getStream(imageUrl);                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);                    // 圖片下載成功重新緩衝並執行回調重新整理介面                    if (bitmap != null) {                        // 加入到軟引用中                        imageCaches.put(imageUrl, new SoftReference<Bitmap>(                                bitmap));                        // 緩衝到檔案系統                        fileUtil.saveBitmap(filepath, bitmap);                        Message msg = new Message();                        msg.what = 111;                        msg.obj = bitmap;                        handler.sendMessage(msg);                    }                }            };            threadPools.execute(thread);        }        return null;    }    public void shutDownThreadPool() {        if (threadPools != null) {            threadPools.shutdown();            threadPools = null;        }    }    /**     * 圖片下載完成回調介面     *      */    public interface ImageDownloadCallBack {        void onImageDownloaded(ImageView imageView, Bitmap bitmap);    }}

 第二種解釋:

今天在做圖片非同步載入時出現了一個載入的圖片與實際位置不符而且出現閃動載入的情況,原來是listView第一個公用元素和滑動後第一個可見元素共用一個convertView導致的在非同步載入的時候重複使用了convertView的緣故,解決方案為為每個imageView控制項設定url內容為tag標記,然後在要更新imageview控制項之前先將該控制項的tag與傳入的url作比對

 

 

在非同步任務完成的方法中判斷該imageView控制項是否為我們要設定的控制項

 部落格轉載自:

http://www.cnblogs.com/lesliefang/p/3619223.html

http://blog.csdn.net/fanjunjian1991/article/details/8742748

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.