1

I have created an custom ListView in which i display image as an list Item. The image i display as a list item is stored in my InternalStorage of the device, What i notice is when number of item is more then seven the list view scroll become slow and the items in the ListView jerk while scrolling. Is anything wrong in my code, or miss something to do in my code. Please help me to solve this out.

Code for displaying List View item

public class ListViewAdapter extends BaseAdapter 
    {
        private Activity activity;
        private ArrayList<String> fileNameArray;
        private LayoutInflater inflater=null;
        public  ImageLoader imageLoader; 

        public ListViewAdapter(Activity a,  ArrayList<String> d) 
        {
            activity = a;
            fileNameArray = d;
            inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            imageLoader = new ImageLoader(activity.getApplicationContext());
        }

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

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

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

        public class ViewHolder
        {
            ImageView image;
        }               

        public View getView(final int position, View convertView, ViewGroup parent) 
        {
            ViewHolder holder;

            if(convertView == null)
            {
                holder = new ViewHolder();
                convertView = inflater.inflate(R.layout.page_list_item, null);

                holder.image = (ImageView)convertView.findViewById(R.id.image); 

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



            // Load image from internalstoarage and display in image view   

            imageLoader.DisplayImage(m_DirectoryPath.getPath() + "/" + fileNameArray.get(position), holder.image, true);

            return convertView;
        }
}

Code for image loader class

public class ImageLoader 
{
    MemoryCache memoryCache = new MemoryCache();
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService; 
    Context m_context;
    public ImageLoader(Context context)
    {
        m_context = context;
        executorService = Executors.newFixedThreadPool(5);
    }

    public void DisplayImage(String url, ImageView imageView, boolean useCachedImage)
    {
        imageViews.put(imageView, url);
        Bitmap bitmap = memoryCache.get(url);

        if(bitmap!=null && useCachedImage)
        {
            imageView.setImageBitmap(bitmap);
        }
        else
        {
            queuePhoto(url, imageView);
        }
    }

    private void queuePhoto(String url, ImageView imageView)
    {
        PhotoToLoad p = new PhotoToLoad(url, imageView);
        executorService.submit(new PhotosLoader(p));
    }

    private Bitmap getBitmap(String url) 
    {
        File filePath = new File(url);

        // from internal storage cache
        Bitmap b = decodeFile(filePath);
        if (b != null)
        {
            return b;
        }
        return null;
    }

    // decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File filePath)
    {
        try 
        {
            // decode image size
             int Required_Height = 0;

             BitmapFactory.Options option = new BitmapFactory.Options();            
             Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(filePath), null, option);

             WindowManager wm = (WindowManager) m_context.getSystemService(Context.WINDOW_SERVICE);
             Display display = wm.getDefaultDisplay();

             if(display.getWidth() == 480)
             {
                 Required_Height = 150;
             }
             else if(display.getWidth() == 800)
             {
                 Required_Height = 200;
             }

             bitmap = Bitmap.createBitmap(bitmap, 0, 0, option.outWidth, Required_Height);

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

    class PhotosLoader implements Runnable 
    {
        PhotoToLoad photoToLoad;
        PhotosLoader(PhotoToLoad photoToLoad)
        {
            this.photoToLoad = photoToLoad;
        }

        @Override
        public void run() 
        {
            if(imageViewReused(photoToLoad))
                return;

            Bitmap bmp = getBitmap(photoToLoad.url);
            memoryCache.put(photoToLoad.url, bmp);

            if(imageViewReused(photoToLoad))
                return;

            BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
            Activity a = (Activity)photoToLoad.imageView.getContext();
            a.runOnUiThread(bd);
        }
    }

    boolean imageViewReused(PhotoToLoad photoToLoad)
    {
        String tag = imageViews.get(photoToLoad.imageView);

        if(tag == null || !tag.equals(photoToLoad.url))         
            return true;

        return false;
    }

    // Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        PhotoToLoad photoToLoad;
        public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
        public void run()
        {
            if(imageViewReused(photoToLoad))
                return;

            if(bitmap != null)
                photoToLoad.imageView.setImageBitmap(bitmap);
        }
    }

    public void clearCache() 
    {
        memoryCache.clear();
    }

}
Rahul
  • 1,667
  • 6
  • 21
  • 38

2 Answers2

0

You have to setPriority for PhotosLoader thread object. I am doing it in constructor like this.

// Make the background thread low priority. This way it will not affect the UI performance
mImageRequstController.setPriority(Thread.NORM_PRIORITY - 1);

By make priority less then normal , it will not effect the UI and will continue its working in background.

Arslan Anwar
  • 18,746
  • 19
  • 76
  • 105
0

What's happening is that, for speed reasons, list items out of view are not loaded until needed. When the user scrolls the list item into view, it is loaded. However, loading an image from the SD card is a time consuming action, enough that the user can see a lag.

Your code loads the images on the UI thread, blocking it from updating and smoothly scrolling the list. Instead, you should be loading the images on another thread, while the list item on the UI thread should display only a progress spinner or some other loading indicator until the image is loaded. And, like the list, the threads should only be started once the list item is in view, not all at once.

AlbeyAmakiir
  • 2,217
  • 6
  • 25
  • 48
  • I look at many example for loading UI in seperate thread, but not able to get how can i implement it in my code..can u help me to know how can i do it in my code – Rahul Aug 23 '12 at 07:19
  • @RahulBorah: No, I won't show you the code. I'd be encouraging you to copy-paste without learning if I did. I'd prefer you to understand it. However, it's fairly trivial to do. Set the image to be a loading progress image. Start a thread that simply loads the image into a Bitmap or whatever. When the thread is done, it calls a Handler which puts the image in the view. That's all. – AlbeyAmakiir Aug 27 '12 at 01:49