The problem here is race between ImageView and your DisplayImage function. As the list is scrolling up or down the rows get reused, so when a remote image arrives it often happens that its target view already belongs to a different row. The only reliable way of doing that is making you own class RemoteImageView extends ImageView which handles fetching the image internally.
Then RemoteImageView can do proper synchronization Let your queuePhoto method take a RemoteLoadListener instead of ImageView, like this: public interface RemoteLoadListener { void onLoadFail(String url); void onLoadSuccess(String url, Bitmap bmp); } Then make your RemoteImageView a RemoteLoadListener and do all the stuff internally: public class RemoteImageView extends ImageView implements RemoteLoadListener { final int static stub_id=R.drawable. Progress; public void displayImage(String url, Activity activity, int size) { myUrl = url; if(cache. ContainsKey(url)) { ... setImageBitmap(cache.
Get(url)); } else { ... imageView. StartAnimation(rotation); queuePhoto(url, activity, this); } } @Override public void onLoadSuccess(String url, Bitmap bmp) { if (url. Equals(myUrl)) { setImageBitmap(bmp); } else { /* the arrived bitmap is stale.Do nothing.
*/ } } @Override public void onLoadFail(String url) { if (url. Equals(myUrl)) { setImageBitmap(placeholder); } else { /* the failed bitmap is stale.Do nothing. */ } } } So in your getView method you simply do this: holder.
Img1. DisplayImage(t1. GetString("image_small"), act, IMAGE_SIZE) UPDATE Try first reducing complexity of the system.
I've removed your photo queue and cache and replaced it with downloading the image from the web (this piece is based on your code). I didn't run this code, I just typed it. But it should give you a clear idea public class RemoteImageView extends ImageView implements RemoteLoadListener { public RemoteImageView(Context context, AttributeSet attrs) { super(context, attrs); } public RemoteImageView(Context context) { super(context); } public void displayRemoteImage(String url, Activity activity, int size) { this.
MyUrl = url; this. Size=size; Resources r = activity.getResources(); int dip = (int)TypedValue. ApplyDimension(TypedValue.
COMPLEX_UNIT_DIP, size, r. GetDisplayMetrics()); LayoutParams layoutParams = this.getLayoutParams(); layoutParams. Height = dip; layoutParams.
Width = dip; this. SetLayoutParams(layoutParams); { rotation = AnimationUtils. LoadAnimation(activity, R.drawable.
Progress); rotation. SetRepeatCount(Animation. INFINITE); this.
StartAnimation(rotation); queuePhoto(url, activity, this); } } @Override public void onLoadSuccess(String url, Bitmap bmp) { if (url. Equals(myUrl)) { setImageBitmap(bmp); } else { /* the arrived bitmap is stale. Do nothing.
*/ } } @Override public void onLoadFail(String url) { if (url. Equals(myUrl)) { setImageBitmap(((BitmapDrawable)getResources(). GetDrawable(stub_id)).getBitmap()); } else { /* the failed bitmap is stale.
Do nothing. */ } } String myUrl; private void queuePhoto(final String url, final RemoteLoadListener listener) { new AsyncTask() { @Override protected Bitmap doInBackground(Void... params) { //from web Bitmap bitmap = null; InputStream is = null; OutputStream os = null; try { is = new URL(url).openStream(); os = new FileOutputStream(f); Utils. CopyStream(is, os); bitmap = decodeFile(f); } catch (Exception ex){ bitmap = null; } finally { if (is!
= null) try { is.close(); } catch (IOException e) { /* ignore */ }; if (os! = null) try { os.close(); } catch (IOException e) { /* ignore */ }; } return bitmap; } @Override protected void onPostExecute(Bitmap result) { if (result! = null) { listener.
OnLoadSuccess(url, result); } else { listener. OnLoadFail(url); } }; }.execute(); } } PS: Note the try - finally block when working with streams.
The problem here is race between ImageView and your DisplayImage function. As the list is scrolling up or down the rows get reused, so when a remote image arrives it often happens that its target view already belongs to a different row. The only reliable way of doing that is making you own class RemoteImageView extends ImageView which handles fetching the image internally.
Then RemoteImageView can do proper synchronization. Let your queuePhoto method take a RemoteLoadListener instead of ImageView, like this: public interface RemoteLoadListener { void onLoadFail(String url); void onLoadSuccess(String url, Bitmap bmp); } Then make your RemoteImageView a RemoteLoadListener and do all the stuff internally: public class RemoteImageView extends ImageView implements RemoteLoadListener { final int static stub_id=R.drawable. Progress; public void displayImage(String url, Activity activity, int size) { myUrl = url; if(cache.
ContainsKey(url)) { ... setImageBitmap(cache. Get(url)); } else { ... imageView. StartAnimation(rotation); queuePhoto(url, activity, this); } } @Override public void onLoadSuccess(String url, Bitmap bmp) { if (url.
Equals(myUrl)) { setImageBitmap(bmp); } else { /* the arrived bitmap is stale. Do nothing. */ } } @Override public void onLoadFail(String url) { if (url.
Equals(myUrl)) { setImageBitmap(placeholder); } else { /* the failed bitmap is stale. Do nothing. */ } } } So in your getView method you simply do this: holder.
Img1. DisplayImage(t1. GetString("image_small"), act, IMAGE_SIZE); UPDATE Try first reducing complexity of the system.
I've removed your photo queue and cache and replaced it with downloading the image from the web (this piece is based on your code). I didn't run this code, I just typed it. But it should give you a clear idea.
Public class RemoteImageView extends ImageView implements RemoteLoadListener { public RemoteImageView(Context context, AttributeSet attrs) { super(context, attrs); } public RemoteImageView(Context context) { super(context); } public void displayRemoteImage(String url, Activity activity, int size) { this. MyUrl = url; this. Size=size; Resources r = activity.getResources(); int dip = (int)TypedValue.
ApplyDimension(TypedValue. COMPLEX_UNIT_DIP, size, r. GetDisplayMetrics()); LayoutParams layoutParams = this.getLayoutParams(); layoutParams.
Height = dip; layoutParams. Width = dip; this. SetLayoutParams(layoutParams); { rotation = AnimationUtils.
LoadAnimation(activity, R.drawable. Progress); rotation. SetRepeatCount(Animation.
INFINITE); this. StartAnimation(rotation); queuePhoto(url, activity, this); } } @Override public void onLoadSuccess(String url, Bitmap bmp) { if (url. Equals(myUrl)) { setImageBitmap(bmp); } else { /* the arrived bitmap is stale.Do nothing.
*/ } } @Override public void onLoadFail(String url) { if (url. Equals(myUrl)) { setImageBitmap(((BitmapDrawable)getResources(). GetDrawable(stub_id)).getBitmap()); } else { /* the failed bitmap is stale.
Do nothing. */ } } String myUrl; private void queuePhoto(final String url, final RemoteLoadListener listener) { new AsyncTask() { @Override protected Bitmap doInBackground(Void... params) { //from web Bitmap bitmap = null; InputStream is = null; OutputStream os = null; try { is = new URL(url).openStream(); os = new FileOutputStream(f); Utils. CopyStream(is, os); bitmap = decodeFile(f); } catch (Exception ex){ bitmap = null; } finally { if (is!
= null) try { is.close(); } catch (IOException e) { /* ignore */ }; if (os! = null) try { os.close(); } catch (IOException e) { /* ignore */ }; } return bitmap; } @Override protected void onPostExecute(Bitmap result) { if (result! = null) { listener.
OnLoadSuccess(url, result); } else { listener. OnLoadFail(url); } }; }.execute(); } } PS: Note the try - finally block when working with streams.
Yep, that is the way to go. – Audrius Jul 18 at 17:53 i'm implementing now to verify. Instead of imageView.
StartAnimation, I think you mean this. StartAnimation ya? – hunterp Jul 18 at 18:29 Sorry, I implemented an ImageView with a background loading thread, but this approach does not prevent any race conditions – hunterp Jul 18 at 18:52 Are you sure you didn't miss url.
Equals(myUrl) check? This is the key part. And, of course, onLoadSuccess/onLoadFail callbacks must be called on UI thread.
If you do everything right it WILL work, GUARANTEED. – JBM Jul 18 at 21:11 1 You just don't need it. BitmapDisplayer does the job which is done in the onLoadSuccess/onLoadFail methods, but because it does it from outside it's out of sync.
Instead, you should call onLoadSuccess in the place where you'd call BitmapDisplayer. – JBM Jul 187 at 8:13.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.