Reputation: 2221
I've my ListView with images on my activity, works great till I add the LRU Cache. When I load for the first time (if i don't scroll it) while the images are loading the first element change its image between all the images i was loading.
My LRU Cache looks like
public class Image {
private static LruCache<String, Bitmap> mMemoryCache = null;
private static int cacheSize = 1024 * 1024 * 10;
private static class AsyncLoader extends AsyncTask<String, Void, Bitmap> {
private ImageView mTarget;
public AsyncLoader(ImageView target) {
this.mTarget = target;
}
@Override
protected void onPreExecute() {
mTarget.setTag(this);
}
@Override
protected Bitmap doInBackground(String...urls) {
String url = urls[0];
Bitmap result = null;
if (url != null) {
result = load(url);
if (result != null) {
mMemoryCache.put(url, result);
}
}
return result;
}
@Override
protected void onPostExecute(Bitmap result) {
if (mTarget.getTag() == this) {
mTarget.setTag(null);
if (result != null) mTarget.setImageBitmap(result);
}
}
}
public static Bitmap load(String urlString) {
if (urlString == null || urlString.length() == 0) return null;
Bitmap bitmap = null;
URL url = null;
try {
url = new URL(urlString);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
public static void loadToView(String url, ImageView view) {
if (url == null || url.length() == 0) return;
if (mMemoryCache == null) {
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return (bitmap.getRowBytes() * bitmap.getHeight());
}
};
}
Bitmap bitmap = getBitmapFromMemCache(url);
if (bitmap == null) {
final AsyncLoader task = (AsyncLoader) new AsyncLoader(view);
view.setTag(task);
task.execute(url);
} else {
view.setImageBitmap(bitmap);
}
}
}
Usage:
Image.loadToView("http://image_url", (ImageView) view.findViewById(R.id.img_thumbnail));
This is how looks my BaseAdapter getView
@Override
public View getView(final int position, View view, ViewGroup parent) {
if (view == null) view = inflater.inflate(R.layout.list_item, null);
((TextView) view.findViewById(R.id.txt_name)).setText(filtered_data.get(position).name);
Image.loadToView("http://graph.facebook.com/" + filtered_data.get(position).id + "/picture", (ImageView) view.findViewById(R.id.img_thumbnail));
return view;
}
Upvotes: 1
Views: 2400
Reputation: 2221
I it figure out reading a lot about image on listview issues, its related to the listview recycling, some related post:
What I've added is the else statement in the onPostExecute method, if the tag isn't the one setted on tag I cancel it.
The fixed code its
public class Image {
private static LruCache<String, Bitmap> mMemoryCache = null;
private static int cacheSize = 1024 * 1024 * 10;
private static class AsyncLoader extends AsyncTask<String, Void, Bitmap> {
private ImageView mTarget;
public AsyncLoader(ImageView target) {
this.mTarget = target;
}
@Override
protected void onPreExecute() {
mTarget.setTag(this);
}
@Override
protected Bitmap doInBackground(String...urls) {
String url = urls[0];
Bitmap result = null;
if (url != null) {
result = load(url);
if (result != null) {
mMemoryCache.put(url, result);
}
}
return result;
}
@Override
protected void onPostExecute(Bitmap result) {
if (mTarget.getTag() == this) {
mTarget.setTag(null);
if (result != null) mTarget.setImageBitmap(result);
} else if (mTarget.getTag() != null) {
((AsyncLoader) mTarget.getTag()).cancel(true);
mTarget.setTag(null);
}
}
}
public static Bitmap load(String urlString) {
if (urlString == null || urlString.length() == 0) return null;
Bitmap bitmap = null;
URL url = null;
try {
url = new URL(urlString);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
public static void loadToView(String url, ImageView view) {
if (url == null || url.length() == 0) return;
if (mMemoryCache == null) {
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return (bitmap.getRowBytes() * bitmap.getHeight());
}
};
}
Bitmap bitmap = getBitmapFromMemCache(url);
if (bitmap == null) {
final AsyncLoader task = (AsyncLoader) new AsyncLoader(view);
view.setTag(task);
task.execute(url);
} else {
view.setImageBitmap(bitmap);
}
}
}
I think google need to fix this or provide a much cleaner workaround, because of the (in my opinion) failure of google on build something where developers only need to care about build their own apps and not to lose their time finding ways to fix the sdk issues
Upvotes: 3