4

I have a custom ListView adapter which implements an ImageThreadLoader class. Unfortunately the class doesn't enable a cache option-download the images from the web and save them as cache.

And then I found this LazyList or here really useful, it behaves quite the same like my ImageThreadLoader class but it's able to save the images as cache. So, I want to implement its ImageLoader class to my current custom ListView adapter.

Unfortunately the structure of my codes and the Lazylist's is quite different, resulting some conflicts on my attempts. For example, the LazyList use array of strings for the image URL, in the other hand I use JSON as the source of image URL.

That's why I need a help here to adapt my ListView adapter to this ImageLoader class.

Here are the codes:

ImageLoader Class which I want implement to my custom ListView adapter:

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(),"Android/data/LazyList");
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }

    final int stub_id=R.drawable.stub;
    public void DisplayImage(String url, Activity activity, ImageView imageView)
    {
        if(cache.containsKey(url))
            imageView.setImageBitmap(cache.get(url));
        else
        {
            queuePhoto(url, activity, imageView);
            imageView.setImageResource(stub_id);
        }    
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView)
    {
        //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);
        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.
            final int REQUIRED_SIZE=70;
            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    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 PhotoToLoad(String u, ImageView i){
            url=u; 
            imageView=i;
        }
    }

    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;
            }
        }
    }

    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();
                        }
                        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);
                            Activity a=(Activity)photoToLoad.imageView.getContext();
                            a.runOnUiThread(bd);
                        }
                    }
                    if(Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                //allow thread to exit
            }
        }
    }

    PhotosLoader photoLoaderThread=new PhotosLoader();

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageView imageView;
        public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}
        public void run()
        {
            if(bitmap!=null)
                imageView.setImageBitmap(bitmap);
            else
                imageView.setImageResource(stub_id);
        }
    }

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

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

}

the custom list view adapter from the LazyList project:

public class LazyAdapter extends BaseAdapter {

    private Activity activity;
    private String[] data;
    private static LayoutInflater inflater=null;
    public ImageLoader imageLoader; 

    public LazyAdapter(Activity a, String[] d) {
        activity = a;
        data=d;
        inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        imageLoader=new ImageLoader(activity.getApplicationContext());
    }

    public int getCount() {
        return data.length;
    }

    public Object getItem(int position) {
        return position;
    }

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

    public static class ViewHolder{
        public TextView text;
        public ImageView image;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        View vi=convertView;
        ViewHolder holder;
        if(convertView==null){
            vi = inflater.inflate(R.layout.item, null);
            holder=new ViewHolder();
            holder.text=(TextView)vi.findViewById(R.id.text);;
            holder.image=(ImageView)vi.findViewById(R.id.image);
            vi.setTag(holder);
        }
        else
            holder=(ViewHolder)vi.getTag();

        holder.text.setText("item "+position);
        holder.image.setTag(data[position]);
        imageLoader.DisplayImage(data[position], activity, holder.image);
        return vi;
    }
}

and here's my custom ListView adapter: ProjectAdapter class

public class ProjectAdapter extends ArrayAdapter<Project> {

    int resource;
    String response;
    Context context;
    private final static String TAG = "MediaItemAdapter";

    private ImageThreadLoader imageLoader = new ImageThreadLoader();

    //Initialize adapter
    public ProjectAdapter(Context context, int resource, List<Project> items) {
        super(context, resource, items);
        this.resource=resource;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        TextView textTitle;
        final ImageView image;

        Project pro = getItem(position);

        LinearLayout projectView;
      //Inflate the view
        if(convertView==null)
        {
            projectView = new LinearLayout(getContext());
            String inflater = Context.LAYOUT_INFLATER_SERVICE;
            LayoutInflater vi;
            vi = (LayoutInflater)getContext().getSystemService(inflater);
            vi.inflate(resource, projectView, true);
        }
        else
        {
            projectView = (LinearLayout) convertView;
        }

        try {
          textTitle = (TextView)projectView.findViewById(R.id.txt_title);
          image = (ImageView)projectView.findViewById(R.id.image);
        } catch( ClassCastException e ) {
          Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e);
          throw e;
        }


        Bitmap cachedImage = null;
        try {
          cachedImage = imageLoader.loadImage(pro.smallImageUrl, new ImageLoadedListener() {
          public void imageLoaded(Bitmap imageBitmap) {
          image.setImageBitmap(imageBitmap);
          notifyDataSetChanged();                }
          });
        } catch (MalformedURLException e) {
          Log.e(TAG, "Bad remote image URL: " + pro.smallImageUrl, e);
        }

        textTitle.setText(pro.project_title);

        if( cachedImage != null ) {
          image.setImageBitmap(cachedImage);
        }

        return projectView;
    }

}

Thank you very much!!

EDIT

UPDATED: ProjectList Activity

    public class ProjectsList extends Activity {
        /** Called when the activity is first created. */
        //ListView that will hold our items references back to main.xml
        ListView lstTest;
        //Array Adapter that will hold our ArrayList and display the items on the ListView
        ProjectAdapter arrayAdapter;
        ProgressDialog dialog;
        //List that will  host our items and allow us to modify that array adapter
        ArrayList<Project> prjcts=null;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.projects_list);
            //Initialize ListView
            lstTest= (ListView)findViewById(R.id.lstText);

             //Initialize our ArrayList
            prjcts = new ArrayList<Project>();
            //Initialize our array adapter notice how it references the listitems.xml layout
            arrayAdapter = new ProjectAdapter(ProjectsList.this, R.layout.listitems,prjcts,ProjectsList.this);

            //Set the above adapter as the adapter of choice for our list
        lstTest.setAdapter(arrayAdapter);

        if (isOnline())
        {
        //Instantiate the Web Service Class with he URL of the web service not that you must pass
        //WebService webService = new WebService("http://notalentrocks.com/myplaceapp/projects.json");
        WebService webService = new WebService("http://liebenwald.spendino.net/admanager/dev/android/projects.json");

        //Pass the parameters if needed , if not then pass dummy one as follows
        Map<String, String> params = new HashMap<String, String>();
        params.put("var", "");

        //Get JSON response from server the "" are where the method name would normally go if needed example
        // webService.webGet("getMoreAllerts", params);
        String response = webService.webGet("", params);

        try
        {
             dialog = ProgressDialog.show(ProjectsList.this, "", "Fetching Projects...", true);
             dialog.setCancelable(true);
             dialog.setCanceledOnTouchOutside(true);
             dialog.setOnCancelListener(new OnCancelListener() {
                public void onCancel(DialogInterface dialog) {

                }
             });
            //Parse Response into our object
            Type collectionType = new TypeToken<ArrayList<Project>>(){}.getType();

            //JSON expects an list so can't use our ArrayList from the lstart
            List<Project> lst= new Gson().fromJson(response, collectionType);


            //Now that we have that list lets add it to the ArrayList which will hold our items.
            for(Project l : lst)
            {
                prjcts.add(l);
                ConstantData.projectsList.add(l);
            }

            //Since we've modified the arrayList we now need to notify the adapter that
            //its data has changed so that it updates the UI
            arrayAdapter.notifyDataSetChanged();
            dialog.dismiss();
        }
        catch(Exception e)
        {
            Log.d("Error: ", e.getMessage());
        }
        }  
        lstTest.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {              
                Intent care = new Intent(ProjectsList.this, ProjectDetail.class);
                care.putExtra("spendino.de.ProjectDetail.position",position);
                startActivity(care);
            }
        });

    }

    protected boolean isOnline() {
        ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo != null && netInfo.isConnected()) {
            return true;
        } else {
             AlertDialog.Builder alertbox = new AlertDialog.Builder(this);
             alertbox.setTitle("spendino Helfomat");
             alertbox.setMessage ("Please check your internet connection");
             alertbox.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                 public void onClick(DialogInterface dialog, int id) {
                      //Main.this.finish();
                 }
             });
             alertbox.show();
            return false;
        }
    }



}

UPDATED Here's my stacktrace:

05-12 11:36:52.670: ERROR/AndroidRuntime(299): Caused by: java.lang.NullPointerException
05-12 11:36:52.670: ERROR/AndroidRuntime(299):     at android.content.ContextWrapper.getCacheDir(ContextWrapper.java:183)
05-12 11:38:29.386: ERROR/AndroidRuntime(324):     at spendino.de.ImageLoader.<init>(ImageLoader.java:41)
05-12 11:36:52.670: ERROR/AndroidRuntime(299):     at spendino.de.Main.<init>(Main.java:56)

ImageLoader 41 is: cacheDir=context.getCacheDir(); Main 56 is: private ImageLoaderCache imageLoader = new ImageLoaderCache(Main.this);

Main.java

public class Main extends Activity { /** Called when the activity is first created. */

ArrayList<Project> prjcts=null;
private final static String TAG = "MediaItemAdapter";
ImageLoader imageLoader;
private Activity activity;
ImageView image1;
ImageView image2;
ImageView image3;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    if (isOnline())
    {


    prjcts = new ArrayList<Project>();
    WebService webService = new WebService("http://liebenwald.spendino.net/admanager/dev/android/projects.json");
    Map<String, String> params = new HashMap<String, String>();
    params.put("var", "");
    String response = webService.webGet("", params);

    imageLoader = new ImageLoader(Main.this);
    try
    {
        Type collectionType = new TypeToken<ArrayList<Project>>(){}.getType();
        List<Project> lst= new Gson().fromJson(response, collectionType);
        for(Project l : lst)
        {
            prjcts.add(l);
            ConstantData.projectsList.add(l);
        }
    }
    catch(Exception e)
    {
        Log.d("Error: ", e.getMessage());
    }

    try {
        image1 = (ImageView)findViewById(R.id.top1);
        image2 = (ImageView)findViewById(R.id.top2);
        image3 = (ImageView)findViewById(R.id.top3);
      } catch( ClassCastException e ) {
        Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e);
        throw e;
      }

      //randomize the index of image entry

      int max = prjcts.size();
      List<Integer> indices = new ArrayList<Integer>(max);
      for(int c = 1; c < max; ++c)
      {
          indices.add(c);
      }

      Random r = new Random();
      int arrIndex = r.nextInt(indices.size());
      int randomIndex1 = indices.get(arrIndex);
      indices.remove(arrIndex);

      int arrIndex2 = r.nextInt(indices.size());
      int randomIndex2 = indices.get(arrIndex2);
      indices.remove(arrIndex2);

      int arrIndex3 = r.nextInt(indices.size());
      int randomIndex3 = indices.get(arrIndex3);
      indices.remove(arrIndex3);


      imageLazy(image1, prjcts.get(randomIndex1));
      imageLazy(image2, prjcts.get(randomIndex2));
      imageLazy(image3, prjcts.get(randomIndex3));

      image1.setOnClickListener(new RandomClickListener(randomIndex1));
      image2.setOnClickListener(new RandomClickListener(randomIndex2));
      image3.setOnClickListener(new RandomClickListener(randomIndex3));
    } 

    final Button project = (Button) findViewById(R.id.btn_projectslist);
    final Button infos = (Button) findViewById(R.id.btn_infos);
    final Button contact = (Button) findViewById(R.id.btn_contact);
    project.setOnClickListener(project_listener);
    infos.setOnClickListener(infos_listener);
    contact.setOnClickListener(contact_listener);
}

/*
 * isOnline - Check if there is a NetworkConnection
 * @return boolean
 */
protected boolean isOnline() {
    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = cm.getActiveNetworkInfo();
    if (netInfo != null && netInfo.isConnected()) {
        return true;
    } else {
         AlertDialog.Builder alertbox = new AlertDialog.Builder(this);
         alertbox.setTitle("spendino Helfomat");
         alertbox.setMessage ("Bitte überprüfen Sie Ihre Internetverbindung");
         alertbox.setPositiveButton("OK", new DialogInterface.OnClickListener() {
             public void onClick(DialogInterface dialog, int id) {
                  Main.this.finish();
             }
         });
         alertbox.show();
        return false;
    }
}

public static class ViewHolder{
    public ImageView image;
}

public void imageLazy(final ImageView image,Project pro)
{
    imageLoadery.displayImage(pro.smallImageUrl, activity, image);
}

public void setImage(Bitmap cachedImage, final ImageView image, Project pro)
{
    try {
        cachedImage = imageLoader.loadImage(pro.smallImageUrl, new ImageLoadedListener() 
        {
            public void imageLoaded(Bitmap imageBitmap)
            {
                image.setImageBitmap(imageBitmap);              
            }
        });
    } catch (MalformedURLException e) {
        Log.e(TAG, "Bad remote image URL: " + pro.smallImageUrl, e);
    }
    if( cachedImage != null ) {
        image.setImageBitmap(cachedImage);
      }
}

public class RandomClickListener implements View.OnClickListener
{
    private final int randomIndex;

    public RandomClickListener(final int randomIndex)
    {
        this.randomIndex = randomIndex;
    }
    @Override
    public void onClick(View v)
    {
        Intent top = new Intent(Main.this, ProjectDetail.class);
        top.putExtra("spendino.de.ProjectDetail.position", randomIndex);
        startActivity(top);
    }
}

Stacktrace:

05-12 13:48:12.606: ERROR/AndroidRuntime(433):     at spendino.de.ImageLoaderCache$PhotosLoader.run(ImageLoaderCache.java:244)
Community
  • 1
  • 1
hectichavana
  • 1,436
  • 13
  • 41
  • 71

4 Answers4

6

Seems that you did not use any view holder in your adapter

I have made changes in your classes.Hope it will works for you with out any difficulties

The ImageThreadLoader class

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Stack;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;

public class ImageThreadLoader {

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

    /** The cache dir. */
    private File cacheDir;

    /**
     * Instantiates a new image loader.
     *
     * @param context the context
     */
    public ImageThreadLoader(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(),"cache_dir_img");
        else
            cacheDir=context.getCacheDir();
        if(!cacheDir.exists())
            cacheDir.mkdirs();
    }
    //This is used for a stub when the user can not see the actual image..
    //this images will be seen
    final int stub_id =R.drawable.sample_image;


    /**
     * Display image.
     *
     * @param url the url
     * @param activity the activity
     * @param imageView the image view
     */
    public void displayImage(String url, Activity activity, ImageView imageView)
    {
        if(cache.containsKey(url))
            imageView.setImageBitmap(cache.get(url));
        else
        {
            queuePhoto(url, activity, imageView);
            imageView.setImageResource(stub_id);
        }    
    }

    /**
     * Queue photo.
     *
     * @param url the url
     * @param activity the activity
     * @param imageView the image view
     */
    private void queuePhoto(String url, Activity activity, ImageView imageView)
    {
        //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);
        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();
    }

    /**
     * Gets the bitmap.
     *
     * @param url the url
     * @return the bitmap
     */
    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);
            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
    /**
     * Decode file.
     *
     * @param f the f
     * @return the bitmap
     */
    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.
            final int REQUIRED_SIZE=70;
            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                scale++;
            }

            //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
    /**
     * The Class PhotoToLoad.
     */
    private class PhotoToLoad
    {

        /** The url. */
        public String url;

        /** The image view. */
        public ImageView imageView;

        /**
         * Instantiates a new photo to load.
         *
         * @param u the u
         * @param i the i
         */
        public PhotoToLoad(String u, ImageView i){
            url=u; 
            imageView=i;
        }
    }

    /** The photos queue. */
    PhotosQueue photosQueue=new PhotosQueue();

    /**
     * Stop thread.
     */
    public void stopThread()
    {
        photoLoaderThread.interrupt();
    }

    //stores list of photos to download
    /**
     * The Class PhotosQueue.
     */
    class PhotosQueue
    {

        /** The photos to load. */
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

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

    /**
     * The Class PhotosLoader.
     */
    class PhotosLoader extends Thread {

        /* (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        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();
                        }
                        Bitmap bmp=getBitmap(photoToLoad.url);
                        cache.put(photoToLoad.url, bmp);
                        if(((String)photoToLoad.imageView.getTag()).equals(photoToLoad.url)){
                            BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
                            Activity a=(Activity)photoToLoad.imageView.getContext();
                            a.runOnUiThread(bd);
                        }
                    }
                    if(Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                //allow thread to exit
            }
        }
    }

    /** The photo loader thread. */
    PhotosLoader photoLoaderThread=new PhotosLoader();

    //Used to display bitmap in the UI thread
    /**
     * The Class BitmapDisplayer.
     */
    class BitmapDisplayer implements Runnable
    {

        /** The bitmap. */
        Bitmap bitmap;

        /** The image view. */
        ImageView imageView;

        /**
         * Instantiates a new bitmap displayer.
         *
         * @param b the b
         * @param i the i
         */
        public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}

        /* (non-Javadoc)
         * @see java.lang.Runnable#run()
         */
        public void run()
        {
            if(bitmap!=null)
                imageView.setImageBitmap(bitmap);
            else
              imageView.setImageResource(stub_id);
        }
    }

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

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

     public static void copyStream(InputStream is, OutputStream os) {
            final int buffer_size=1024;
            try
            {
                byte[] bytes=new byte[buffer_size];
                for(;;)
                {
                  int count=is.read(bytes, 0, buffer_size);
                  if(count==-1)
                      break;
                  os.write(bytes, 0, count);
                }
            }
            catch(Exception ex){}
        }


}

Now your ProjectAdapter class

import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ProjectAdapter extends ArrayAdapter<Project> {

    int resource;
    String response;
    Context context;
    List<Project> items;
    private ImageThreadLoader imageLoader;
    LayoutInflater mInflater;
    Activity activity;

    // Initialize adapter
    public ProjectAdapter(Context context, int resource, List<Project> items,
            Activity activity) {
        super(context, resource, items);
        this.resource = resource;
        imageLoader = new ImageThreadLoader(context);
        this.items = items;
        mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.activity = activity;
    }

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

        ViewHolder holder = null;

        // Inflate the view
        if (convertView == null) {

            convertView = mInflater.inflate(resource, null);
            holder = new ViewHolder();
            holder.image = (ImageView) convertView.findViewById(R.id.image);
            holder.textTitle = (TextView) convertView
                    .findViewById(R.id.txt_title);
            convertView.setTag(holder);

        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Project project = items.get(position);

        holder.textTitle.setText(project.project_title);

        String imageurl = project.smallImageUrl;
        holder.image.setTag(imageurl);
        imageLoader.displayImage(imageurl, activity, holder.image);
        return convertView;

    }

    static class ViewHolder {

        TextView textTitle;

        ImageView image;
    }

And at last when you set the adapter in your ListView after getting the web data, use this

your_listview.setAdapter(your_ProjectAdapter_instance );
your_ProjectAdapter_instance .notifyDataSetChanged();
Tanmay Mandal
  • 39,873
  • 12
  • 51
  • 48
  • nice! It works like a charm..thank you. Now I'm wondering to make it accessible without internet connection. Can you help me with it? I just updated my question and attached my ProjectsList class. Thanks a lot – hectichavana May 12 '11 at 09:12
  • Can you give me more details about your requirement. – Tanmay Mandal May 12 '11 at 10:07
  • When there's an internet connection, the app will download the json data (images and text) as cache into the SD card (works). But when there's no internet connection, the app will download the images and text from the cache instead of from the Json file on the internet. In other words, the images and text for the list should be fetched from the cache everytime there's no internet connection. – hectichavana May 12 '11 at 10:33
  • You can not do that currently ,for that you have to save the last data you have fetched from web in your application ,it may be `SharedPreference` or `SqlLite database` .Then when you did not find the internet connection just get the data from the persistent storage and show them in your list.Hope its clears to you. – Tanmay Mandal May 12 '11 at 10:47
  • hm okay, I'll find out. Thank you. I have one more question, so I made this method to fetch only the image to an acitivty: ImageLoader imageLoader = new ImageLoader(Main.this); private Activity activity; public void imageLazy(final ImageView image,Project pro) { imageLoader.displayImage(pro.smallImageUrl, activity, image); } but then the app always crashes everytime I open the activity. Do you have any idea why? – hectichavana May 12 '11 at 11:19
  • please have a look at my question, I've pasted the stackstrace. Thx – hectichavana May 12 '11 at 11:42
  • This is the line where you getting this error `private ImageLoader imageLoader = new ImageLoader(Main.this);` ..Declare it first like `ImageLoader imageLoader;` and in your `onCreate` use this `imageLoader = new ImageLoader(Main.this);`..It will solve your problem – Tanmay Mandal May 12 '11 at 13:00
  • Now, the stacktrace shows me the error is here on the ImageLoader: if(((String)photoToLoad.imageView.getTag()).equals(photoToLoad.url)){ – hectichavana May 12 '11 at 13:48
  • there you go, check out my question – hectichavana May 12 '11 at 13:51
  • Three things here can be null in your `imageView`,`url`,`tag`.Please check it via a debugger .Thanks and Best Of Luck. – Tanmay Mandal May 12 '11 at 13:56
  • I tried comment out this line: //if(((String)photoToLoad.imageView.getTag()).equals(photoToLoad.url))then the app works. I think what null is the tag. Any idea to make the tag not null? – hectichavana May 12 '11 at 13:59
1

i do have the sam eproblem of different datatype other than String array but I am using Arraylist(but not JASON) so I do changes in the code of Fedor and below is my implementation, hope this help

LazyAdapter

public LazyAdapter(Activity a, ArrayList<?> mStatus)
{
    activity = a;
    data = mStatus;
    inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    imageLoader = new ImageLoader(activity.getApplicationContext());
}

public int getCount()
{
    if(data != null  && data.size() > 15)
        return data.size();
    else
        return 15;
}

I found that even if the files in sdcard it doesnt load becuase its checking for that object which goes null if you change the activity on which list is implemented so I changed getBitmap() code also ImageLoader

private Bitmap getBitmap(String urlString)
{
    String filename = String.valueOf(urlString.substring(urlString.lastIndexOf("/") + 1));
    File f = new File(cacheDir, filename);
    try
    {
        if(!f.exists())
        {
            Bitmap bitmap = null;
            InputStream is = new URL(urlString).openStream();
            OutputStream os = new FileOutputStream(f);
            Globals.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        }
        else
        {               
            Bitmap bitmap = decodeFile(f);
            return bitmap;
        }

    }
    catch (Exception ex)
    {
        ex.printStackTrace();
        BitmapDrawable mDrawable = (BitmapDrawable) context.getResources().getDrawable(R.drawable.placeholder);
        return mDrawable.getBitmap();
    }
}
ingsaurabh
  • 15,249
  • 7
  • 52
  • 81
  • I'm giving your code a try right now, btw what is Globals on your getBitmap method? and where do you declare the context? thx – hectichavana May 10 '11 at 18:44
  • I tried to match your getBitmap with Fedor's getBitmap, I found out that Globals in yours is Utils in Fedor, that makes sense. I'm just curious where do you declare the context to avoid the null? – hectichavana May 10 '11 at 18:51
  • If you asking for imageloader its just the same as Fedors one and passed in constructor only I had changed only getBitmap() function – ingsaurabh May 11 '11 at 04:34
  • there's 'context' here: BitmapDrawable mDrawable = (BitmapDrawable) context.getResources().getDrawable(R.drawable.placeholder); did you only declare it as global variable? – hectichavana May 11 '11 at 08:29
  • yes at class level in Image loader and passing context in constructor of it then in constructor this.context = context; – ingsaurabh May 11 '11 at 08:41
  • Okay, I just wanna make sure, I've just edited my question....pls have a look. Because this is very crucial, got something to do with my work. Thanks a lot – hectichavana May 11 '11 at 12:30
  • you can take a look at my question, I've just updated it with my latest ImageLoader Class and Adapter.. everytime I run the ListActivity, the app crashed. Can you tell me where did I go wrong?Thx – hectichavana May 11 '11 at 14:39
  • there can be many reasons for the crash it will be east to trace what causes problem if you put your logcat info – ingsaurabh May 12 '11 at 04:30
0

Stackoverflow member Fedor posted some source for a cached ImageLoader in answer to this question. Android - How do I do a lazy load of images in ListView The source is here source. His answer got 100+ upvotes. I found it worked very well caching Flickr images of mine. You should be able to adapt it for your use.

Community
  • 1
  • 1
NickT
  • 23,844
  • 11
  • 78
  • 121
  • yes I know his, have checked it out as well. But it's pretty complicated if I have to adapt his code to what I'm having right. There are some conflicts. Or do you mind helping me adapt his LazyList adapter to mine? If so I'll give this 50 reputations – hectichavana May 05 '11 at 13:50
0

You haven't set the image tag when sending into the lazy loader class.

 Bitmap cachedImage = null;
    try {

      cachedImage = imageLoader.loadImage(pro.smallImageUrl, new ImageLoadedListener() {
      public void imageLoaded(Bitmap imageBitmap) {
      image.setImageBitmap(imageBitmap);
      notifyDataSetChanged();                }
      });
    } catch (MalformedURLException e) {
      Log.e(TAG, "Bad remote image URL: " + pro.smallImageUrl, e);
    }

What is private ImageThreadLoader imageLoader = new ImageThreadLoader(); ? Can I see that class?

Hades
  • 3,916
  • 3
  • 34
  • 74