Question

I have a customized list view in my application, which is showing an image and text (Json). The image I am getting from URL is working, but listview is laggy (scrolling lag). Maybe my problem is ImageLoader.java class This is my code:

public class ImageLoader {
MemoryCache memoryCache = new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews = Collections
        .synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;

public ImageLoader(Context context) {
    fileCache = new FileCache(context);
    executorService = Executors.newFixedThreadPool(5);
}

final int stub_id = R.drawable.aaaaaaaaaaaaaaa;

public void DisplayImage(String url, ImageView imageView) {
    imageViews.put(imageView, url);
    Bitmap bitmap = memoryCache.get(url);
    if (bitmap != null)
        imageView.setImageBitmap(bitmap);
    else {
        queuePhoto(url, imageView);
        imageView.setImageResource(stub_id);
    }
}

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

private Bitmap getBitmap(String url) {
    File f = fileCache.getFile(url);

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

    // from web
    try {
        Bitmap bitmap = null;
        URL imageUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) imageUrl
                .openConnection();
        conn.setConnectTimeout(30000);
        conn.setReadTimeout(30000);
        conn.setInstanceFollowRedirects(true);
        InputStream is = conn.getInputStream();
        OutputStream os = new FileOutputStream(f);
        Utils.CopyStream(is, os);
        os.close();
        bitmap = decodeFile(f);
        return bitmap;
    } catch (Throwable ex) {
        ex.printStackTrace();
        if (ex instanceof OutOfMemoryError)
            memoryCache.clear();
        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;
        FileInputStream stream1 = new FileInputStream(f);
        BitmapFactory.decodeStream(stream1, null, o);
        stream1.close();

        // 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;
        FileInputStream stream2 = new FileInputStream(f);
        Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
        stream2.close();
        return bitmap;
    } catch (FileNotFoundException e) {
    } catch (IOException e) {
        e.printStackTrace();
    }
    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() {
        try {
            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);
        } catch (Throwable th) {
            th.printStackTrace();
        }
    }
}

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);
        else
            photoToLoad.imageView.setImageResource(stub_id);
    }
}

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

}

This is a also my BaseAdapter.java class

public class BRIgeAdapter extends BaseAdapter {
private LayoutInflater inflater;
private Activity activity;
public ArrayList<HashMap<String, String>> data;
public ViewHolder holder;
public ImageLoader imageLoader;
HashMap<String, String> itemList;
private int screenSize;
MainActivity main;
public BRIgeAdapter(Activity a, ArrayList<HashMap<String, String>> d,int screenSize) {
    this.activity = a;
    this.data = d;
    this.screenSize = screenSize;
    inflater = (LayoutInflater) activity
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    imageLoader = new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
    return data.size();
}
public Object getItem(int position) {
    return data.get(position);
}
public long getItemId(int position) {
    return (long) position;
}
@Override
public int getViewTypeCount() {
    return data.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        holder = new ViewHolder();
        main = new MainActivity();
        convertView = inflater.inflate(R.layout.list_row, parent, false);

        holder.journal = (TextView) convertView
                .findViewById(R.id.smalljournal);
        holder.statId = (TextView) convertView
                .findViewById(R.id.smallstatID);

        holder.smallDescription1 = (TextView) convertView
        .findViewById(R.id.smallDescription1);
        holder.DateTime = (TextView) convertView
                .findViewById(R.id.smallDateTime);
        holder.thumb_image = (ImageView) convertView
                .findViewById(R.id.smallthumb);
        holder.title = (TextView) convertView.findViewById(R.id.smalltitle);
        holder.description = (TextView) convertView
                .findViewById(R.id.smallDescription);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    itemList = data.get(position);
    if (itemList != null) {
        holder.journal.setText(itemList.get(MainActivity.KEY_journal));
        // holder.DateTime.setText(itemList.get(MainActivity.KEY_pubDate));
        holder.statId.setText(itemList.get(MainActivity.KEY_statID));
        holder.smallDescription1.setText(itemList.get(MainActivity.KEY_description));
        holder.journal.setTypeface(MainActivity.tf2);
        String titleString = itemList.get(MainActivity.KEY_title);
        Calendar cal = new GregorianCalendar(2013, 11, 20);
        DateFormat df = new SimpleDateFormat("dd.MM.yyyy");
        // DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        String date = df.format(cal.getTime());
        String DateTime = itemList.get(MainActivity.KEY_pubDate);
        DateTime = date;
        holder.DateTime.setText(DateTime);
        holder.title.setText(titleString);
        holder.title.setTypeface(MainActivity.tf2);

        holder.title.setLineSpacing(2, 1.2f);
        holder.description.setLineSpacing(2, 1.2f);

        if(screenSize == Configuration.SCREENLAYOUT_SIZE_NORMAL)
            holder.description.setVisibility(View.INVISIBLE);
        else
            holder.description.setText(itemList.get(MainActivity.KEY_description));

        holder.description.setTypeface(MainActivity.tf2);
        imageLoader.DisplayImage(itemList.get(MainActivity.KEY_image),
                holder.thumb_image);
    }
    return convertView;
}
static class ViewHolder {
    public TextView journal, title, description,smallDescription1, DateTime, statId;
    ImageView thumb_image;
}
 }

Is there any possible way I can reduce or remove this lagging?

No correct solution

OTHER TIPS

If you are using xml then set the width and height of the ListView to match_parent.

I see that you use Threads to load images from url. I Recommend you to use Android Universal Image Loader Library. It is pretty flexible and less laggy. You can find the whole information Here

Also, it looks like you are creating objects in the getView method. This means that when each view is drawn the OS must go through all the work creating those objects for you. If are working with a large list (or small amt of ram) you will force the garbage collector to run, causing stuttering of your app. Try to move all the object creation to the constructor method so it will only run once. Then just reassign them as needed in your get view method. This should provide a noticable speed improvement.

Good luck ;)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top