【问题标题】:Using cwac-endless adapter to load endless ListView of images使用 cwac-endless 适配器加载无限的 ListView 图像
【发布时间】:2013-03-12 06:23:39
【问题描述】:

我编写了以下代码,该代码使用CWAC Endless Adapter 实现了包含 ImageViews 的 ListView 的无限滚动。使用 AsyncTasks 从 Web 按需检索图像:

package com.myproject.ui.adapter;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;

import com.commonsware.cwac.endless.EndlessAdapter;

public class MyThumbsAdapter extends EndlessAdapter {

    public static class FetchThumbTask extends AsyncTask<String, Void, Bitmap> {

        private final Context mContext;
        private final BaseAdapter mAdapter;
        private final String mThumbId;
        private final View mView;

        public FetchThumbTask(Context context, BaseAdapter adapter, View view, String thumbId) {
            mContext = context;
            mAdapter = adapter;
            mView = view;
            mThumbId = thumbId;
        }

        @Override
        protected Bitmap doInBackground(String... arg0) {
            Bitmap bitmap = null;
            if (cache.get(mThumbId) == null) {
                // Fetch thumbnail
                try {
                    URL url = new URL(...);
                    InputStream is = url.openStream();
                    bitmap = BitmapFactory.decodeStream(is);
                    cache.put(mThumbId, bitmap);
                } catch (Exception e) {
                    ...
                }
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
              // Set the loaded image on the ImageView
                      ImageView imageView = (ImageView) mView.findViewById(R.id.thumb_image);
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }

    public static class MyThumbsBaseAdapter extends BaseAdapter {

        private final Context mContext;
        private final List<String> mThumbIds = new ArrayList<String>();

        public MyThumbsBaseAdapter(Context context) {
            mContext = context;
        }

        public void addThumbIds(List<String> thumbIds) {
            mThumbIds.addAll(thumbIds);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            String thumbId = mThumbIds.get(position);
            View rootView = convertView;

            if (rootView == null) {
                rootView = LayoutInflater.from(parent.getContext()).inflate(
                        R.layout.thumbnails, null);
            }
            ImageView imageView =
                    (ImageView) rootView.findViewById(R.id.doodle_thumb_image);

            Bitmap bitmap = cache.get(thumbId);
            if (bitmap == null) {
                loadThumbBitmap(rootView, thumbId);
            } else if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }

            return rootView;
        }

        @Override
        public int getCount() {
            return mThumbIds.size();
        }

        @Override
        public Object getItem(int position) {
            return null;
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        private void loadThumbBitmap(View view, String thumbId) {
            new FetchThumbTask(mContext, this, view, thumbId)
                .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
        }
    }

    static {
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        cache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // The cache size will be measured in kilobytes rather than
                // number of items.
                return bitmap.getByteCount() / 1024;
            }
        };
    }

    private static final LruCache<String, Bitmap> cache;

    private List<String> mThumbIdsCache;

    public MyThumbsAdapter(Context context) {
        super(new MyThumbsBaseAdapter(context));
    }

    @Override
    protected View getPendingView(ViewGroup parent) {
        return LayoutInflater.from(parent.getContext())
                .inflate(R.layout.loading_thumb, null);
    }

    @Override
    protected boolean cacheInBackground() throws Exception {
        JsonReader reader = // Retrieve thumb ids list from server
        mThumbIdsCache = // Returned thumb ids list
        return true;
    }

    @Override
    protected void appendCachedData() {
        MyThumbsBaseAdapter adapter = (MyThumbsBaseAdapter) getWrappedAdapter();
        adapter.addThumbIds(mThumbIdsCache);
    }
}

我正在使用 LruCache 缓存从 Web 加载的位图。问题是在我的 Nexus 7 上进行测试时,我看到很多缓存未命中,它应该有足够的可用内存。这会导致图像在我向上/向下滚动 ListView 时弹出到位。

更糟糕的是,我看到应用程序崩溃并出现 OutOfMemory 错误,但我无法始终如一地重现。

我在这里做错了什么?我不应该为每个图像触发单独的 AsyncTask 吗?

编辑:我还应该提到我正在下载的图像是预先缩放的。

【问题讨论】:

    标签: android android-listview cwac-endless


    【解决方案1】:

    我认为您需要做的是在屏幕上显示Thumbnails 而不是位图图像。您可以生成Thumbnails 并根据您的尺寸要求显示。并且每当用户点击Thumb时,只需采用原始路径并设置壁纸。

    另一个选项是您可以使用Universal Image Loader,它可以帮助您将图像缓冲在磁盘中(例如SD card 或您的应用程序的Internal memory)。所以Out of Memory的问题可以解决。

    对于显示位图的最佳实践,Displaying Bitmaps Efficiently 会有所帮助。

    编辑:

    为您的应用程序使用以下配置。这会将图像缓存在应用程序的 缓存目录 中。

    File cacheDir = new File(this.getCacheDir(), "cwac");
    if (!cacheDir.exists())
        cacheDir.mkdir();
    
    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
                CWAC.this)
                .threadPoolSize(5)
                .threadPriority(Thread.MIN_PRIORITY + 3)
                .denyCacheImageMultipleSizesInMemory()
                // .memoryCache(new UsingFreqLimitedMemoryCache(2000000)) // You
                // can pass your own memory cache implementation
                .memoryCacheSize(1048576 * 10)
                // 1MB=1048576 *declare 20 or more size if images are more than
                // 200
                .discCache(new UnlimitedDiscCache(cacheDir))
                // You can pass your own disc cache implementation
                //.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
                .build();
    

    【讨论】:

    • 谢谢!编辑:我还应该提到我正在下载的图像是预先缩放的。
    • 我错过了.. 抱歉没看。
    • 不幸的是,使用 ImageLoader 的性能似乎比我上面的 LruCache 解决方案更差,即使在配置了它的 memoryCacheSize 之后也是如此。
    • 查看intexsoft.com/blog/item/68-universal-image-loader-part-1.html 图像加载器教程。良好且易于理解的整个图书馆。你也可以使用 LRU 缓存和其他东西。
    • ImageLoader 在添加一些额外的配置后工作得非常好。剩下的一个问题是我看到了透支。图像有时会在进入视野后闪烁,就像被多次绘制一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多