我已经实现了下面的代码来填充我的列表视图中的各种图像(如果可用)并且它可以工作但问题是当触摸屏幕上的其他元素或滚动快速图像移动时。任何帮助将不胜感激......
import java.lang.ref.WeakReference;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.view.View;
import android.widget.ImageView;
import android.widget.TableLayout;
public class ImageDownloader {
public void download(String url, ImageView imageView, TableLayout imageTable) {
if (cancelPotentialDownload(url, imageView)) {
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView, imageTable);
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
imageView.setImageDrawable(downloadedDrawable);
task.execute(url);
}
}
class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
String url;
private final WeakReference<ImageView> imageViewReference;
private final WeakReference<TableLayout> imageTableReference;
public BitmapDownloaderTask(ImageView imageView, TableLayout imageTable) {
imageViewReference = new WeakReference<ImageView>(imageView);
imageTableReference = new WeakReference<TableLayout>(imageTable);
}
@Override
protected Bitmap doInBackground(String... params) {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(params[0], o);
final int REQUIRED_SIZE=70;
//Find the correct scale value. It should be the power of 2.
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=4;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale++;
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeFile(params[0], o2);
}
@Override
protected void onPostExecute(Bitmap result) {
if (isCancelled()) {
result = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
TableLayout imageTable = imageTableReference.get();
BitmapDownloaderTask bitmapDownloaderTask = ImageDownloader.getBitmapDownloaderTask(imageView);
// Change bitmap only if this process is still associated with it
if (this == bitmapDownloaderTask) {
imageView.setImageBitmap(result);
imageView.setVisibility(View.VISIBLE);
imageTable.setVisibility(View.VISIBLE);
}
}
}
}
static class DownloadedDrawable extends ColorDrawable {
private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;
public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
super(Color.BLACK);
bitmapDownloaderTaskReference =
new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
}
public BitmapDownloaderTask getBitmapDownloaderTask() {
return bitmapDownloaderTaskReference.get();
}
}
private static boolean cancelPotentialDownload(String url, ImageView imageView) {
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
if (bitmapDownloaderTask != null) {
String bitmapUrl = bitmapDownloaderTask.url;
if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
bitmapDownloaderTask.cancel(true);
} else {
// The same URL is already being downloaded.
return false;
}
}
return true;
}
private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
if (imageView != null) {
Drawable drawable = imageView.getDrawable();
if (drawable instanceof DownloadedDrawable) {
DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
return downloadedDrawable.getBitmapDownloaderTask();
}
}
return null;
}
}
答案 0 :(得分:1)
好的,你的问题在同一时间非常简单和复杂。简而言之,ListView回收视图。如果使用回收视图,则会将位图设置为现有或新的imageView。换句话说,当您下载位图时,它将被设置为可能与您正在显示的项目不对应的图像视图。
在我的应用程序中,我为我的imageview设置了一个标签。下载完成后,如果找不到视图,我会执行listview.findViewByTag(tag)我只是不设置位图。如果发现我设置它。
在getView中,如果内存中存在Bitmap,我将位图设置为我的图像视图。如果位图不在内存中,我启动后台作业,并为该URL设置标记到我的视图。
该作业将从磁盘(如果存在)或从Internet加载位图。
一旦存在位图,我就使用requestCode,标签和位图调用我的活动回调。
在你的情况下,标签和位图就足够了。
然后你必须在listview上做findViewByTag。如果找到视图,则设置位图,如果找不到视图,请忘记它,当项目出现后将调用getView时,位图应存在于内存或磁盘上。
因此,您不能指望列表视图中的视图是您第一次显示项目时的视图。我还要说,如果你启动后台线程,你应该将你的图像drawable设置为null,这样你就不会看到回收视图的位图。
答案 1 :(得分:1)
正如Loic所提到的,基本问题是ListView回收用于呈现列表行的View对象。因此,传递给download()方法的ImageView引用可以重新用于列表中的不同“位置”,这会导致您观察到的行为。
我解决这个问题的方法是让Download任务采用List Adapter而不是ImageView作为参数,并在下载完成后让它在适配器上调用notifyDataSetChanged()。下载任务将可绘制的图像添加到缓存中,并且适配器的getView()实现可以简单地从缓存中获取Drawable以设置ImageView。
我已经描述了这种方法并在此处发布了代码:http://mobrite.com/lazy-loading-images-in-a-listview/