我有一个列表适配器,其中图像加载有旋转进度动画,直到从网上获取它们为止.当我在ListVIEw中向下滚动时,似乎动画旋转了上一张图像…这是一个非常奇怪且令人讨厌的效果.唯一应该旋转的是进度动画.
我使用一个称为ImageLoader的类来异步netfetch图像:
final int stub_ID=R.drawable.progress;public voID displayImage(String url, Activity activity, ImageVIEw imageVIEw,int size){ //Log.d("ImageLoader" , url); this.size=size; if(cache.containsKey(url)) { //If this works, then why do the olD images still spin?? imageVIEw.clearanimation(); //imageVIEw.setBackgroundDrawable(null); //imageVIEw.setimageDrawable(null); imageVIEw.setimageBitmap(cache.get(url)); } else { rotation = AnimationUtils.loadAnimation(activity, R.drawable.progress); rotation.setRepeatCount(Animation.INFINITE); imageVIEw.startAnimation(rotation); queuePhoto(url, activity, imageVIEw); //imageVIEw.setimageResource(stub_ID); //imageVIEw.setBackgroundResource(stub_ID); } Resources r = activity.getResources(); int dip = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, r.getdisplayMetrics()); LayoutParams layoutParams = imageVIEw.getLayoutParams(); layoutParams.height = dip; layoutParams.wIDth = dip; imageVIEw.setLayoutParams(layoutParams);}
现在您可能会问,queuePhoto是做什么的?这是动画开始的唯一其他地方.
//Used to display bitmap in the UI threadclass Bitmapdisplayer implements Runnable{ Bitmap bitmap; ImageVIEw imageVIEw; public Bitmapdisplayer(Bitmap b, ImageVIEw i){bitmap=b;imageVIEw=i;} public voID run() { if(bitmap!=null) { imageVIEw.clearanimation(); imageVIEw.setimageBitmap(bitmap); } else { imageVIEw.clearanimation(); rotation = AnimationUtils.loadAnimation(context, R.drawable.progress); rotation.setRepeatCount(Animation.INFINITE); imageVIEw.startAnimation(rotation); //imageVIEw.setimageResource(stub_ID); } }}
现在,我的列表适配器的getVIEw的一部分:
@OverrIDe public VIEw getVIEw(int position, VIEw convertVIEw, VIEwGroup parent) { if( convertVIEw == null ){ vi = inflater.inflate(R.layout.trending_item, null); holder=new VIEwHolder(); holder.img1 = (ImageVIEw) vi.findVIEwByID(R.ID.t_item_1); holder.img2 = (ImageVIEw) vi.findVIEwByID(R.ID.t_item_2); holder.img3 = (ImageVIEw) vi.findVIEwByID(R.ID.t_item_3); vi.setTag(holder); } else { holder=(VIEwHolder)vi.getTag(); } JsONObject t1= ts.get(1); if(t1 !=null) { holder.img1.setTag(t1.getString("image_small")); imageLoader.displayImage(t1.getString("image_small"), act, holder.img1,IMAGE_SIZE); holder.img1.setTag(TAG_T t1.getString("ID")); holder.img1.setonClickListener(ocl); } else { holder.img1.setimageDrawable(null); holder.img1.setBackgroundDrawable(null); holder.img1.setTag(null); holder.img1.setTag(TAG_T, null); }
更新
我实现了JBM提出的解决方案,但是我无法使其工作,他建议在UI线程上运行.
public class RemoteImageVIEw extends ImageVIEw implements RemoteLoadListener {final int stub_ID=R.drawable.progress;int size;private HashMap<String, Bitmap> cache=new HashMap<String, Bitmap>();Animation rotation;private file cacheDir;public RemoteImageVIEw(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);}public RemoteImageVIEw(Context context, AttributeSet attrs) { super(context, attrs); //Make the background thead low priority. This way it will not affect the UI performance photoloaderThread.setPriority(Thread.norM_PRIORITY-1); //Find the dir to save cached images if (androID.os.Environment.getExternalStorageState().equals(androID.os.Environment.MEDIA_MOUNTED)) cacheDir=new file(androID.os.Environment.getExternalStorageDirectory(),context.getString(R.string.app_name)); else cacheDir=context.getCacheDir(); if(!cacheDir.exists()) cacheDir.mkdirs();}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); if(cache.containsKey(url)) { this.clearanimation(); setimageBitmap(cache.get(url)); } else { rotation = AnimationUtils.loadAnimation(activity, R.drawable.progress); rotation.setRepeatCount(Animation.INFINITE); this.startAnimation(rotation); queuePhoto(url, activity, this); }}@OverrIDepublic voID onl oadSuccess(String url, Bitmap bmp) { if (url.equals(myUrl)) { setimageBitmap(bmp); } else { /* the arrived bitmap is stale. do nothing. */ }}@OverrIDepublic voID onl oadFail(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(String url, Activity activity, ImageVIEw imageVIEw){ //This ImageVIEw may be used for other images before. So there may be some old tasks in the queue. We need to discard them. photosQueue.Clean(imageVIEw); PhotoToload p=new PhotoToload(url, imageVIEw); synchronized(photosQueue.photosToload){ photosQueue.photosToload.push(p); photosQueue.photosToload.notifyAll(); } //start thread if it's not started yet if(photoloaderThread.getState()==Thread.State.NEW) photoloaderThread.start();}PhotosQueue photosQueue=new PhotosQueue();public voID stopThread(){ photoloaderThread.interrupt();}//stores List of photos to downloadclass PhotosQueue{ private Stack<PhotoToload> photosToload=new Stack<PhotoToload>(); //removes all instances of this ImageVIEw public voID Clean(ImageVIEw image) { for(int j=0 ;j<photosToload.size();){ if(photosToload.get(j).imageVIEw==image) photosToload.remove(j); else ++j; } }}//Task for the queueprivate class PhotoToload{ public String url; public ImageVIEw imageVIEw; public PhotoToload(String u, ImageVIEw i){ url=u; imageVIEw=i; }}class Photosloader extends Thread { public voID run() { try { while(true) { //thread waits until there are any images to load in the queue if(photosQueue.photosToload.size()==0) synchronized(photosQueue.photosToload){ photosQueue.photosToload.wait(); } if(photosQueue.photosToload.size()!=0) { PhotoToload photoToload; synchronized(photosQueue.photosToload){ photoToload=photosQueue.photosToload.pop(); } Bitmap bmp=getBitmap(photoToload.url); cache.put(photoToload.url, bmp); Object tag=photoToload.imageVIEw.getTag(); if(tag!=null && ((String)tag).equals(photoToload.url)){ Bitmapdisplayer bd=new Bitmapdisplayer(bmp, photoToload.imageVIEw); Activity a=(Activity)photoToload.imageVIEw.getContext(); a.runOnUiThread(bd); } } if(Thread.interrupted()) break; } } catch (InterruptedException e) { //allow thread to exit } }}Photosloader photoloaderThread=new Photosloader();//Used to display bitmap in the UI threadclass Bitmapdisplayer implements Runnable{ Bitmap bitmap; ImageVIEw imageVIEw; public Bitmapdisplayer(Bitmap b, ImageVIEw i){bitmap=b;imageVIEw=i;} public voID run() { if(bitmap!=null) { imageVIEw.clearanimation(); imageVIEw.setimageBitmap(bitmap); } else { imageVIEw.clearanimation(); imageVIEw.setimageResource(stub_ID); } }}private Bitmap getBitmap(String url) { System.out.println("GET: " +url); if(url== null) { return null; } //I IDentify images by hashcode. Not a perfect solution, good for the demo. String filename=String.valueOf(url.hashCode()); file f=new file(cacheDir, filename); //from SD cache Bitmap b = decodefile(f); if(b!=null) return b; //from web try { Bitmap bitmap=null; inputStream is=new URL(url).openStream(); OutputStream os = new fileOutputStream(f); Utils.copyStream(is, os); os.close(); bitmap = decodefile(f); return bitmap; } catch (Exception ex){ ex.printstacktrace(); return null; }}//decodes image and scales it to reduce memory consumptionprivate Bitmap decodefile(file f){ try { //decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(new fileinputStream(f),null,o); //Find the correct scale value. It should be the power of 2. int wIDth_tmp=o.outWIDth, height_tmp=o.outHeight; int scale=1; while(true){ if(wIDth_tmp/2<size || height_tmp/2<size) break; wIDth_tmp/=2; height_tmp/=2; scale*=2; } //decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize=scale; return BitmapFactory.decodeStream(new fileinputStream(f), null, o2); } catch (fileNotFoundException e) { Log.d("ImageLoader",e.getMessage(),e); } return null;}
}
解决方法:
这里的问题是ImageVIEw与displayImage函数之间的竞争.当列表向上或向下滚动时,行将被重用,因此,当远程映像到达时,通常会发生其目标视图已经属于另一行的情况.唯一可靠的方法是让您拥有自己的类RemoteImageVIEw来扩展ImageVIEw,该ImageVIEw可在内部处理图像.然后,RemoteImageVIEw可以进行适当的同步.
让您的queuePhoto方法采用RemoteLoadListener代替ImageVIEw,如下所示:
public interface RemoteLoadListener { voID onl oadFail(String url); voID onl oadSuccess(String url, Bitmap bmp);}
然后将RemoteImageVIEw设置为RemoteLoadListener并在内部执行所有 *** 作:
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 onl oadSuccess(String url, Bitmap bmp) { if (url.equals(myUrl)) { setimageBitmap(bmp); } else { /* the arrived bitmap is stale. do nothing. */ } } @OverrIDe public voID onl oadFail(String url) { if (url.equals(myUrl)) { setimageBitmap(placeholder); } else { /* the Failed bitmap is stale. do nothing. */ } }}
因此,在您的getVIEw方法中,您只需执行以下 *** 作:
holder.img1.displayImage(t1.getString("image_small"), act, IMAGE_SIZE);
更新
首先尝试降低系统的复杂性.我已经删除了您的照片队列和缓存,并将其替换为从Web上下载图像(此部分基于您的代码).我没有运行这段代码,只是输入了它.但这应该给您一个清晰的想法.
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 onl oadSuccess(String url, Bitmap bmp) { if (url.equals(myUrl)) { setimageBitmap(bmp); } else { /* the arrived bitmap is stale. do nothing. */ } } @OverrIDe public voID onl oadFail(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<VoID, VoID, Bitmap>() { @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:请注意尝试-在使用流时最终阻塞.
总结以上是内存溢出为你收集整理的android-ListAdapter任务不可能吗?将ImageViews和Animations结合在一起吗?全部内容,希望文章能够帮你解决android-ListAdapter任务不可能吗?将ImageViews和Animations结合在一起吗?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)