I'm using a gridview to display hundreds of images (perhaps even a few thousand). The images are located on a server and I'm caching the images using HttpResponseCache. The problem I'm having is that when I swipe down through the gridview, the recycled views are showing 3 or more images, per child view, before finally settling on the correct image. It seems to be a result of the callback methods returning all the requested images. How can I get a gridview to not have this giant swoosh of activity when scrolling up/down.
getView method of my custom adapter
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (convertView == null) {
v = li.inflate(R.layout.folder_button, null);
} else {
v = convertView;
}
TextView tv = (TextView)v.findViewById(R.id.tvFolderButtonTitle);
tv.setText(mBaseItems[position].Name);
tv.setTextColor(Color.WHITE);
ImageView iv = (ImageView)v.findViewById(R.id.ivFolderButtonImage);
iv.setLayoutParams(new LinearLayout.LayoutParams(folderWidth_, folderHeight_));
iv.setScaleType(ImageView.ScaleType.FIT_XY);
String imageUrl = "http://path.to.image";
api_.GetImageAsync(imageUrl, new GetImageStreamCallback(iv), false);
return v;
}
callback method that sets the image.
public class GetImageStreamCallback implements IApiCallback {
private ImageView currentImageView;
public GetImageStreamCallback(ImageView imageView) {
currentImageView = imageView;
}
public void Execute(Object data) {
if (data != null) {
try {
Bitmap image = (Bitmap) data;
currentImageView.setImageBitmap(image);
} catch (Exception e) {
Log.i("Exception", "Error getting image");
}
}
}
}
custom AsyncTask called from api_.GetImageAsync above
public class AsyncRequestImage extends AsyncTask<String,String,Object > {
HttpURLConnection connection_;
InputStream inStream_;
IApiCallback callback_;
boolean ignoreCache_;
public AsyncRequestImage(IApiCallback callback, boolean ignoreCache) {
this.callback_ = callback;
this.ignoreCache_ = ignoreCache;
}
#Override
protected Object doInBackground(String... uri) {
Bitmap image;
if (ignoreCache_) {
image = acquireImage(uri[0], true);
} else {
image = acquireImage(uri[0], false);
if (image == null)
image = acquireImage(uri[0], true);
}
return image;
}
#Override
protected void onPostExecute(Object image) {
callback_.Execute(image);
}
private Bitmap acquireImage(String url, boolean ignoreCache) {
try {
URL _url = new URL(url);
connection_ = (HttpURLConnection) _url.openConnection();
connection_.addRequestProperty("Accept-Encoding", "gzip");
if (ignoreCache) {
connection_.setRequestProperty("Cache-Control", "max-age=0");
} else {
connection_.addRequestProperty("Cache-Control", "only-if-cached");
}
connection_.connect();
String encoding = connection_.getContentEncoding();
// Determine if the stream is compressed and uncompress it if needed.
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
try {
inStream_ = new GZIPInputStream(connection_.getInputStream());
} catch (FileNotFoundException e) {
}
} else {
try {
inStream_ = connection_.getInputStream();
} catch (FileNotFoundException e) {
}
}
if (inStream_ != null) {
try {
Bitmap image = BitmapFactory.decodeStream(inStream_);
return image;
} catch (java.lang.OutOfMemoryError oom) {
FileLogger.getFileLogger().ReportInfo("UrlConnection: Bitmap creation failed. Out of memory");
}
}
} catch (IOException e) {
if (e != null && e.getMessage() != null) {
Log.i("AsyncRequestImage doInBackground:",e.getMessage());
}
} finally {
connection_.disconnect();
}
return null;
}
}
Part of the issue I was having was due to an unoptimized BaseAdapter.GetView
Also when the user initiated a fling gesture, I was still trying to load all the images as the views passed by.
This article! Provided a detailed description and solution for each of the mistakes I was making. Also in that article is a link to source code that provides a method to stop loading images until the fling gesture has finished.
Related
I'm solving my problem about Image Loader and I have some problems..
What I want is to show many images (about 400) in GridView(or ListView).
I don't want to use the Library like Picasso, Glide like that.
and Here is the problem.
When I call the method which convert from url to bitmap?
3.1. before setAdapter, then pass the bitmap array.
3.2. while getView.
two things are working well. but too much slow... maybe cuz of the times to call URLConnection..
Could anyone help me about these problem? How can I speed up? or are there any other solution without Open Source.
Here is my Source.
Now, 3-1.
ShowImage
private void showImages(ArrayList<String> imgUrls) {
ArrayList<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < imgUrls.size(); i++) {
try {
String img_path = imgUrls.get(i);
Bitmap bitmap = new UriToBitmapAsyncTask().execute(img_path).get();
bitmaps.add(bitmap);
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
CustomAdapter adapter = new CustomAdapter(getApplicationContext(),R.layout.row,bitmaps);
gridView.setAdapter(adapter);
}
and This is the customAdapter's GetView
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflator.inflate(rowLayout, parent, false);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.imageView.setImageBitmap(bitmaps.get(position));
return convertView;
}
You should really take Reinventing the wheel to heart but if you really want to toture yourself an Approach could be:
use a ThreadPoolExecutor to fetch more images at once, you should read up how to use them
implement a way to cancel threads who load a img for a griditem which isn't displayed anymore
use two sets of data a thumbnail which loads faster for the grid view and a real image which gets loaded when the user clicks on the grid
dont't forget to use a LRU caching method or your device will run out of memory depending on the images
Don't use ArrayList to store bitmaps. Bitmaps usually take consumes a lot of memory. Try using LRUCache like this way,
public class TCImageLoader implements ComponentCallbacks2 {
private TCLruCache cache;
public TCImageLoader(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
int maxKb = am.getMemoryClass() * 1024;
int limitKb = maxKb / 8; // 1/8th of total ram
cache = new TCLruCache(limitKb);
}
public void display(String url, ImageView imageview, int defaultresource) {
imageview.setImageResource(defaultresource);
Bitmap image = cache.get(url);
if (image != null) {
imageview.setImageBitmap(image);
}
else {
new SetImageTask(imageview).execute(url);
}
}
private class TCLruCache extends LruCache<String, Bitmap> {
public TCLruCache(int maxSize) {
super(maxSize);
}
#Override
protected int sizeOf(ImagePoolKey key, Bitmap value) {
int kbOfBitmap = value.getByteCount() / 1024;
return kbOfBitmap;
}
}
private class SetImageTask extends AsyncTask<String, Void, Integer> {
private ImageView imageview;
private Bitmap bmp;
public SetImageTask(ImageView imageview) {
this.imageview = imageview;
}
#Override
protected Integer doInBackground(String... params) {
String url = params[0];
try {
bmp = getBitmapFromURL(url);
if (bmp != null) {
cache.put(url, bmp);
}
else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
return 0;
}
return 1;
}
#Override
protected void onPostExecute(Integer result) {
if (result == 1) {
imageview.setImageBitmap(bmp);
}
super.onPostExecute(result);
}
private Bitmap getBitmapFromURL(String src) {
try {
URL url = new URL(src);
HttpURLConnection connection
= (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
}
#Override
public void onLowMemory() {
}
#Override
public void onTrimMemory(int level) {
if (level >= TRIM_MEMORY_MODERATE) {
cache.evictAll();
}
else if (level >= TRIM_MEMORY_BACKGROUND) {
cache.trimToSize(cache.size() / 2);
}
}
}
get a instance of TCImageLoader and call display method appropriately.
Hello StackOverflowers,
I developed project which is using Volley for communicating with REST web API. I use complex image loading process.
I want to load user's facebook profile picture and sometimes I want it first to get storage cached image and sometimes I want it to be always from internet.
The problem is that image url from facebook is always the same. This is why I made a basic check if image file size differs from the cached one.
I was using Volley's ImageLoader and it's cache implementation. Then I found it complex to do it with ImageLoader so I choose to use Picasso but it doesn't show cached image.
I know that for Picasso cache to work I need a custom "Downloader" and I know I can do it with OkHttp but my project is already using Volley for all REST calls.
So is it possible to use Volley as downloader for Picasso and its image cache structure?
Here is how I managed to fix the first issue and only the caching flow is the problem now:
public class PerfectImageLoader {
private static final String TAG = PerfectImageLoader.class.getSimpleName();
private Context mContext;
private SharedPreferences mSharedPreferences;
public PerfectImageLoader(Context context)
throws NullPointerException {
if (context == null) {
throw new NullPointerException("Context cannot be null");
}
mContext = context;
mSharedPreferences = context.getSharedPreferences(
context.getPackageName(), Context.MODE_PRIVATE);
}
public void getImage(final String imageUrl, final ImageView imageView) {
getImage(imageUrl, imageView, false);
}
public void getImage(final String imageUrl, final ImageView imageView,
final boolean isAwlaysFromInternet)
throws NullPointerException {
if (imageView == null) {
throw new NullPointerException("OnImageFromCacheListener cannot be null");
}
if (TextUtils.isEmpty(imageUrl)) {
throw new NullPointerException("image url cannot be null");
}
if (!isAwlaysFromInternet) {
loadCachedImage(imageUrl, imageView, null);
}
calculateFileSize(imageUrl, new OnFileSizeCheck() {
#Override
public void ready(final int networkFileSize) {
int cachedImageSize = mSharedPreferences
.getInt(imageUrl, 0);
TLog.v(TAG, "networkFileSize:" + networkFileSize);
TLog.v(TAG, "cachedImageSize:" + cachedImageSize);
if (cachedImageSize != networkFileSize || cachedImageSize == 0) {
TLog.v(TAG, "cachedImageSize != networkFileSize");
final Callback callback = new Callback() {
#Override
public void onSuccess() {
TLog.v(TAG, "downloaded");
mSharedPreferences.edit()
.putInt(imageUrl, networkFileSize).apply();
}
#Override
public void onError() {
TLog.v(TAG, "error");
if (isAwlaysFromInternet) {
mSharedPreferences.edit()
.remove(imageUrl).apply();
imageView.setImageBitmap(null);
}
}
};
if (isAwlaysFromInternet) {
TLog.v(TAG, "MemoryPolicy.NO_CACHE");
Picasso.with(mContext).load(imageUrl).memoryPolicy(MemoryPolicy.NO_CACHE)
.into(imageView, callback);
} else {
Picasso.with(mContext).load(imageUrl).into(imageView, callback);
}
} else {
TLog.v(TAG, "cachedImageSize == networkFileSize");
loadCachedImage(imageUrl, imageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(mContext).load(imageUrl).into(imageView);
}
});
}
}
});
}
private void loadCachedImage(final String imageUrl, final ImageView imageView,
Callback callback) {
if (callback != null) {
Picasso.with(mContext)
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView, callback);
} else {
Picasso.with(mContext)
.load(imageUrl)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(imageView);
}
}
public static void calculateFileSize(String url, final OnFileSizeCheck fileSizeCallbacks)
throws NullPointerException {
if (fileSizeCallbacks != null && !TextUtils.isEmpty(url)) {
new AsyncTask<String, Void, Integer>() {
#Override
protected Integer doInBackground(String... params) {
Integer fileSize = null;
try {
URL urlObj = new URL(params[0]);
URLConnection urlConnection = urlObj.openConnection();
urlConnection.connect();
fileSize = urlConnection.getContentLength();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return fileSize;
}
#Override
protected void onPostExecute(Integer fileSize) {
super.onPostExecute(fileSize);
if (fileSizeCallbacks != null) {
fileSizeCallbacks.ready(fileSize);
}
}
}.execute(url);
} else {
if (fileSizeCallbacks == null) {
throw new NullPointerException("file size callbacks parameter is null");
}
if (url == null) {
throw new NullPointerException("url parameter is null");
} else if (TextUtils.isEmpty(url)) {
throw new NullPointerException("url parameter is empty");
}
}
}
}
I'm trying to display an image from a url in a "InfoWindowAdapter" ,I have the following code, but does not show me the image. What is wrong please ?
public class ObjectInfoWindow implements GoogleMap.InfoWindowAdapter {
private Activity activity;
private HashMap<String, LostObject> markers;
private Marker markerShowingInfoWindow;
private boolean mRefreshingInfoWindow;
private View v = null;
ImageUrlView imgThumbnail;
public LostObjectInfoWindow(Activity activity, HashMap<String, LostObject> markers) {
this.activity = activity;
this.markers = markers;
}
#Override
public View getInfoContents(Marker marker) {
DebugLog.d("TAG", "getInfoContents mRefreshingInfoWindow "+mRefreshingInfoWindow);
if(v==null){
v = activity.getLayoutInflater().inflate(R.layout.lost_object_info_window, null);
}
LostObject lostObject = markers.get(marker.getId());
if (lostObject != null) {
String imgThumbnailPath = lostObject.getPhoto();
if(imgThumbnailPath==null || imgThumbnailPath.trim().length() == 0){
TextView title = (TextView) v.findViewById(R.id.title);
TextView description = (TextView) v.findViewById(R.id.description);
title.setText(lostObject.getType());
if (lostObject.getContact() != null) {
description.setText(activity.getResources().getString(R.string.lost_object_contact_info_window, lostObject.getContact()));
}
imgThumbnail = (ImageUrlView) v.findViewById(R.id.thumbnail);
imgThumbnail.setScaleType(ImageView.ScaleType.FIT_CENTER);
imgThumbnail.setImageResource(R.drawable.ic_ayn_list_grey);
} else {
if (!mRefreshingInfoWindow) {
TextView title = (TextView) v.findViewById(R.id.title);
TextView description = (TextView) v.findViewById(R.id.description);
title.setText(lostObject.getType());
if (lostObject.getContact() != null) {
description.setText(activity.getResources().getString(R.string.lost_object_contact_info_window, lostObject.getContact()));
}
imgThumbnail = (ImageUrlView) v.findViewById(R.id.thumbnail);
markerShowingInfoWindow = marker;
imgThumbnailPath = imgThumbnailPath.replace(".jpg", "_100_100.jpg");
imgThumbnail.setListener(listener);
imgThumbnail.load(imgThumbnailPath);
}else{
v.invalidate();
}
}
}
// Returning the view containing InfoWindow contents
return v;
}
This method is called after the bitmap has been loaded. It checks if the currently displayed
info window is the same info window which has been saved. If it is, then refresh the windown to display the newly loaded image.
private ImageUrlView.ImageUrlViewListener listener = new ImageUrlView.ImageUrlViewListener() {
#Override
public void imageAdded(ImageUrlView img) {
if (markerShowingInfoWindow != null ) {
mRefreshingInfoWindow = true;
markerShowingInfoWindow.showInfoWindow();
mRefreshingInfoWindow = false;
}
}
};
}
Firstly, I do not see any variable that is storing the URL path/address. Hence there is no way for the program to get the location of the image and display it on to the info window. There are two ways to do that:
One is by using Universal Image Loader which is a library that you can embed in your app project. Which is quite similar to what you are doing in your app code using custominfoadapter. Please refer to this link for the complete implementation of the code.
Another method is to use a separate thread (Async Task is the best option to do that). This will load the image from the URL in the background and glue it inside the custominfowindow in a synchronized manner.
Here is a code snippet for that, Put this method inside your ObjectInfoWindow class and pass in URL and marker as parameters:
protected void handleMarkerClicked(final Marker marker, final String url) {
new AsyncTask<Void, Void, Void>()
{
#Override
protected void onPreExecute()
{
super.onPreExecute();
_infoImageDrawable = null;
}
#Override
protected Void doInBackground(Void... params)
{
InputStream is;
try {
is = (InputStream) new URL(url).getContent();
_infoImageDrawable = Drawable.createFromStream(is, "");
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
marker.showInfoWindow();
}
}.execute();
}
}
Hope this would Help!!
I have the queryAppIcon() method that queries and stores images in the array appIconDrawable. However, I'm only getting blank where images should pop up. Please let me know if I should post any other relevant code
This is the relevant code inside the ViewActivity:
// global vars
final Drawable[] appIconDrawable = null;
int i;
public Drawable[] queryAppIcon() throws ParseException, IOException {
ParseQuery<ParseObject> query = ParseQuery.getQuery("AndroidStoreContent");
query.whereExists("appIcon");
List<ParseObject> ParseResult = query.find();
// Drawable array
appIconDrawable = new Drawable[ParseResult.size()];
for (i = 0; i < ParseResult.size(); i++) {
ParseFile pf = (ParseFile) ParseResult.get(i).get("appIcon");
startDownload(pf);
}
return appIconDrawable;
}
public void startDownload(ParseFile pf) {
new DownloadImageTask(this).execute(pf);
}
public class DownloadImageTask extends AsyncTask<ParseFile, Void, Drawable> {
private AsyncResponse ar;
DownloadImageTask(AsyncResponse ar) {
this.ar = ar;
}
#Override
protected Drawable doInBackground(ParseFile... pf) {
return fetchDrawable(pf[0]);
}
protected void onPostExecute(Drawable result) {
ar.processFinish(result);
}
public Drawable fetchDrawable(ParseFile pf) {
InputStream is;
try {
is = (InputStream) new URL(pf.getUrl()).getContent();
return Drawable.createFromStream(is,null);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
#Override
public void processFinish(Drawable d) {
appIconDrawable[i] = d; // i also tried testing appIconDrawable[1] = d and the app loaded with all blank images and then crashes
}
This is the interface, AsyncResponse:
public interface AsyncResponse {
void processFinish(Drawable d);
}
It seems like you need a bit of refactor...
You are expecting Drawable[] from queryAppIcon() but you will always get an empty set because you start the download which takes place in a separate thread to then update the return value.
You should be setting the Drawable to the ImageView from within processFinish.
Signs of things done wrong: Async methods such as downloading images should never have a return value.
UPDATE
Here is a very simple download AsyncTask but there are many checks, optimizations, etc. missing, like CACHE! Also, ImageView inside DownloadImageTask should be held by a WeakReference (Google it) otherwise it WILL leak your activity.
public class DownloadImageTask extends AsyncTask<String, Void, Drawable> {
private static final String TAG = DownloadImageTask.class.getSimpleName();
private ImageView mImageView;
DownloadImageTask(ImageView imageView) {
mImageView = imageView;
}
#Override
protected Drawable doInBackground(String... url) {
return fetchDrawable(url[0]);
}
#Override
protected void onPostExecute(Drawable result) {
if (result != null) {
mImageView.setImageDrawable(result);
} else {
Log.w(TAG, "Could download image!");
}
}
public static Drawable fetchDrawable(String url) {
Log.v(TAG, "Downloading: " + url);
InputStream is;
try {
is = (InputStream) new URL(url).getContent();
return Drawable.createFromStream(is, null);
} catch (MalformedURLException e) {
Log.e(TAG, e.getMessage(), e);
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
return null;
}
}
Adapter:
public class ImageDownloadAdapter extends ArrayAdapter<String>{
public ImageDownloadAdapter(Context context, String[] objects) {
super(context, R.layout.item_image_download, R.id.txt_url, objects);
}
#SuppressLint("NewApi")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
String url = getItem(position);
ImageView imageView = (ImageView) view.findViewById(R.id.img_download);
DownloadImageTask downloadImageTask = new DownloadImageTask(imageView);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
downloadImageTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
} else {
downloadImageTask.execute(url);
}
return view;
}
}
Activity:
ListView listView = (ListView) findViewById(android.R.id.list);
listView.setAdapter(new ImageDownloadAdapter(this, new String[]{
"http://www.beautystat.com/site/wp-content/uploads/2011/02/happy-faces-small.jpg",
"http://www.ducthide.com/new_wallet_pics/happy_face.JPG"
}));
I know this is asked before and I have already searched a lot about this but could not get any proper answer on my issue.
I have created a list, the list gets filled with data that is coming from JSON. Now inside getView() method, I am inflating my custom row with data. Each row contains a Thumbnail, and I am creating each thumbnail from a different thread.
Now the problem is, everything is going well until I don't scroll my list. When I scroll my list, my getView() method is called continuously , and all thumbnail images are getting recreated and its position is getting shuffled. Once thumbnails are created I don't want to recreate them and also I want to maintain the order of my thumbnails.
Can you please guys help me on this?
Any help will greatly be appreciated.
My getView() method is:
#Override
public View getView(final int position, View convertView,
ViewGroup parent) {
ViewHolder viewHolder = new ViewHolder();
if (convertView == null) {
inflater = getLayoutInflater();
convertView = inflater.inflate(R.layout.list_item_row, parent,
false);
viewHolder.titleText = (TextView) convertView
.findViewById(R.id.titleText);
viewHolder.thumbImage = (ImageView) convertView
.findViewById(R.id.thumbnail);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
final String thumbnailURLString = mPostsList.get(position).get(
"thumb");
createBitmapThread = (Thread) getLastNonConfigurationInstance();
createBitmapThread = new MyThread(thumbnailURLString,
viewHolder.thumbImage);
createBitmapThread.start();
viewHolder.titleText.setText(title);
return convertView;
}
And the Thread class:
public class MyThread extends Thread {
String mThumbURLString = null;
ImageView mImageView = null;
public MyThread(String thumbnailURLString, ImageView imageView) {
mThumbURLString = thumbnailURLString;
mImageView = imageView;
}
#Override
public void run() {
try {
URL newurl = null;
try {
newurl = new URL(mThumbURLString);
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
// Options opts = new Options();
/*
* opts.inDither = true; opts.inInputShareable = true;
* opts.inJustDecodeBounds = true; opts.inPurgeable =
* true;
*/
Log.e("", "INSIDE IMAGE DOINBG");
mBitmap = BitmapFactory
.decodeStream((InputStream) newurl.getContent());
} catch (IOException e) {
e.printStackTrace();
}
mHandler.post(new MyRunnable(mImageView));
} finally {
}
}
public class MyRunnable implements Runnable {
ImageView mImageView = null;
public MyRunnable(ImageView imageView) {
mImageView = imageView;
}
public void run() {
mImageView.setImageBitmap(mBitmap);
}
}
ok , here how i did a similar thing:
for each getView , create a new Fetching class and put it into a concurrent/synchronized stack . in the meanwhile , set the bitmap of the view to be empty (or anything else you wish) . you can also use caching instead.
the class will contain info of how to load the data (for example , the url of the bitmap) , the view to update , and the result of the fetching (for example , the bitmap itself) .
now , back to the getView, create&execute an asyncTask that will use a loop on the stack , each time it gets a Fetching class instance from the stack (once it's empty,the asyncTask will break the loop and finish) , check that the view that needs to be updated still needs the data (using its viewHolder) load the bitmap , set the result into the Fetching class , and pass it through the publishProgress() function.
in the onProgressUpdate method , do the same check as before using the Fetching class instance and the viewHolder . if all went well , update the view to have the bitmap that was fetched .
a nice yet complicated example of how to handle the same problem can be found here .
I was having the same problem and the suggestion above worked perfectly, finally - thanks! Code snippets below:
My AsyncTask:
protected class LoadImageTask extends AsyncTask<String, Void, String> {
private View v;
public LoadImageTask(View v) {
super();
this.v = v;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "<onPostExecute> load image complete. ");
}
#Override
protected String doInBackground(String... addresses) {
String response = "";
Bitmap bm = null;
for (String address : addresses) {
ViewHolder holder = (ViewHolder) v.getTag();
String name = getNameFromMDN(address);
if (name != null && name.length() > 0) {
// get contact ID from name
long id = getContactID(name);
if (id != 0) {
bm = getThumbnailForId(id);
if (bm != null && bm.getHeight() > 0) {
bm = resizeBitmap(bm);
holder.icon.setImageBitmap(bm);
Log.d(TAG, "set thumbnail for " + name + ", id: " + id);
}
}
}
// default icon
if (bm == null) {
Log.d(TAG, "using default icon for mdn: " + address + ", name: " + name);
holder.icon.setImageResource(R.drawable.contact_big);
/*String s = getContactNameByContactId(person, address);
s = s.substring(0, 2);
if (s.length() > 0 && !MDNUtil.isNumber(s)) {
Log.d(TAG, "using dynamic icon for person: " + person + ", mdn: " + address + ", name: " + s);
icon.setBackgroundColor(Color.BLUE);
iconText.setText(s);
iconText.setTextColor(Color.WHITE);
iconText.setVisibility(View.VISIBLE);
} else {
// use generic icon
holder.icon.setImageResource(R.drawable.contact_big);
}*/
}
}
return "";
}
then in the bindVew() of my CustomCursorAdapter:
LoadImageTask load = new LoadImageTask(v);
load.doInBackground(address);
update: what FINALLY WORKED was preventing the recycling of the views in the listview, in my custom cursor adapter:
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public int getViewTypeCount() {
return 500;
}