Imageloader仅在listitem离开屏幕后加载列表中的图像

时间:2012-03-21 10:03:49

标签: android image url adapter

我有一个带有适配器的listView,它通过ImageLoader类从URL加载图像。 问题是,在列表项滚动到屏幕外之前,屏幕上的图像不会显示/加载。

基本上,显示了listView,但在向下滚动并再次向上滚动之前,不会加载任何图像。这适用于列表中的所有项目,在第一次显示它们时,直到您向后滚动它们才会加载图像。

我自己没有写过ImageLoader类,我很难理解为什么图像在第一次显示时没有加载。在队列中尝试了很多不同的东西,但似乎没有任何帮助。

ImageLoader类:

public class ImageLoader {

    //the simplest in-memory cache implementation. This should be replaced with something like SoftReference or BitmapOptions.inPurgeable(since 1.6)
    private HashMap<String, Bitmap> cache=new HashMap<String, Bitmap>();
    private File cacheDir;

    public ImageLoader(Context context){
        //Make the background thead low priority. This way it will not affect the UI performance
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);

        //Find the dir to save cached images
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
            cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }

    final int stub_id=R.drawable.nopic;

    public void DisplayImage(String url, Activity activity, ImageView imageView, ProgressBar progressBar)
    {
        if(cache.containsKey(url)){
            imageView.setImageBitmap(cache.get(url));
            if (progressBar != null){
                progressBar.setVisibility(View.GONE);
                imageView.setVisibility(View.VISIBLE);
            }
        }else{
            queuePhoto(url, activity, imageView, progressBar);

            if(progressBar != null){
                imageView.setVisibility(View.GONE); //ADDED
                progressBar.setVisibility(View.VISIBLE);
            } else {
                imageView.setImageResource(stub_id);
            }
        }    
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView, ProgressBar progressBar)
    {

        //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them. 
        photosQueue.Clean(imageView);
        PhotoToLoad p = new PhotoToLoad(url, imageView, progressBar);
        synchronized(photosQueue.photosToLoad){
            photosQueue.photosToLoad.push(p);
            photosQueue.photosToLoad.notifyAll();
        }

        //start thread if it's not started yet
        if(photoLoaderThread.getState()==Thread.State.NEW)
            photoLoaderThread.start();
    }

    private Bitmap getBitmap(String url) 
    {
        //I identify images by hashcode. Not a perfect solution, good for the demo.
        String filename=String.valueOf(url.hashCode());
        File f=new File(cacheDir, filename);

        //from SD cache
        Bitmap b = decodeFile(f);
        if(b!=null)
            return b;

        //from web
        try {
            Bitmap bitmap=null;
            InputStream is=new URL(url).openStream();
            OutputStream os = new FileOutputStream(f);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    //decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f){
        try {
            //decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(f),null,o);

            //Find the correct scale value. It should be the power of 2.
            int requiredSize = 100;

            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;
            while(true){
                if(width_tmp/2<requiredSize || height_tmp/2<requiredSize)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale*=2;
            }

            //decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize=scale;
            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        } catch (FileNotFoundException e) {}
        return null;
    }

    //Task for the queue
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public ProgressBar progressBar;
        public PhotoToLoad(String u, ImageView i, ProgressBar p){
            url=u; 
            imageView=i;
            progressBar = p;
        }
    }

    PhotosQueue photosQueue=new PhotosQueue();

    public void stopThread(){
        photoLoaderThread.interrupt();
    }

    //stores list of photos to download
    class PhotosQueue
    {
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

        //removes all instances of this ImageView
        public void Clean(ImageView image)
        {
            for(int j=0 ;j<photosToLoad.size();){
                if(photosToLoad.get(j).imageView==image)
                    photosToLoad.remove(j);
                else
                    ++j;
            }
        }
    }

    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    // PHOTOSLOADER: Thread
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    class PhotosLoader extends Thread {
        public void run() {
            try {
                while(true)
                {
                    //thread waits until there are any images to load in the queue
                    if(photosQueue.photosToLoad.size() == 0)
                        synchronized(photosQueue.photosToLoad){
                            photosQueue.photosToLoad.wait();
                        }
                    if(photosQueue.photosToLoad.size() != 0)
                    {
                        PhotoToLoad photoToLoad;
                        synchronized(photosQueue.photosToLoad){
                                                            photoToLoad = photosQueue.photosToLoad.pop();                                                       photosQueue.photosToLoad.remove(photoToLoad);
                        }
                        Bitmap bmp = getBitmap(photoToLoad.url);
                        cache.put(photoToLoad.url, bmp);
                        Object tag = photoToLoad.imageView.getTag();
                        if(tag != null && ((String)tag).equals(photoToLoad.url)){
                            BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad.imageView, photoToLoad.progressBar);
                            Activity a = (Activity)photoToLoad.imageView.getContext();
                            a.runOnUiThread(bd);
                        }
                    }
                    if(Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                //allow thread to exit
            }
        }
    }

    PhotosLoader photoLoaderThread = new PhotosLoader();


    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    // BITMAP DISPLAYER
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable{
        Bitmap      bitmap;
        ImageView   imageView;
        ProgressBar progressBar;
        public BitmapDisplayer(Bitmap b, ImageView i, ProgressBar p){
            bitmap = b; 
            imageView = i; 
            progressBar = p;
        }
        public void run(){
            if(bitmap != null){
                imageView.setImageBitmap(bitmap);
                if (progressBar != null){
                    imageView.setVisibility(View.VISIBLE);
                    progressBar.setVisibility(View.GONE);
                }
            }else{
                if (progressBar != null){
                    progressBar.setVisibility(View.VISIBLE);
                    imageView.setVisibility(View.GONE);
                }
            }
        }
    }

    public void clearCache() {
        //clear memory cache
        cache.clear();

        //clear SD cache
        File[] files = cacheDir.listFiles();
        for(File f:files)
            f.delete();
    }
}

适配器:

public class Adapter_Agenda extends BaseAdapter {

    // DEBUG
    private final String TAG = this.getClass().getSimpleName();

    private LayoutInflater inflater = null;
    public ViewHolder holder;
    private ArrayList<Agenda> agendas;
    UnixTimeToStringConverter unixConverter;
ImageLoader imageLoader;

    View vi;

    public Adapter_Agenda(Context context, ArrayList<Agenda> agendas) {

        this.agendas = agendas;
        unixConverter = new UnixTimeToStringConverter();
         imageLoader = new ImageLoader(context);
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public int getCount() {
        return agendas.size();
    }

    @Override
    public Agenda getItem(int position) {
        return agendas.get(position);
    }

    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {

        vi = convertView;

        if(convertView  ==  null){

            vi = inflater.inflate(R.layout.item_list_agenda, null);
            holder = new ViewHolder();

            holder.header       = (TextView)vi.findViewById(R.id.item_list_header);
                    holder.image            = (ImageView)vi.findViewById(R.id.item_list_image);
            holder.time         = (TextView)vi.findViewById(R.id.item_list_time);
            holder.description  = (TextView)vi.findViewById(R.id.item_list_description);

            vi.setTag(holder);
            holder = (ViewHolder)vi.getTag();
        } else {
            holder = (ViewHolder)vi.getTag();
        }

        holder.header.setText(agendas.get(position).getHeader());
        imageLoader.DisplayImage(agendas.get(position).getImageURL(), activity, holder.image, null);
   holder.time.setText(unixConverter.getUnixDateFormated(agendas.get(position).getStartTime()));

        return vi;
    }

    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    // VIEWHOLDER
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    public class ViewHolder{

        public TextView         time;
            public ImageView                image;
        public TextView         header;
        public TextView         description;

    }
}

2 个答案:

答案 0 :(得分:3)

刚刚放

holder.image.setTag(agendas.get(position).getImageURL());

imageLoader.DisplayImage(agendas.get(position).getImageURL(), activity, holder.image, null);

答案 1 :(得分:2)

图像将在新线程中下载。所以你正在做的是异步文件下载。 问题是你&#34;使用&#34;您的图像在实际下载之前。结果,无法显示图像。

你需要做什么:

在下载图像时创建类似回调的内容。您可以通过创建一个由适配器实现的接口来实现此目的。

每当调用接口方法时,您知道已经下载了图像,您可以通过调用&#34; notifyDatasetChange()&#34;来通知适配器。