25

我已经用谷歌搜索了这个但找不到答案,所以这里......

我有一个显示一些文本和图像的 ListView。底层适配器出于性能原因(根据推荐的方法)回收视图,并且我根据 Android Developers 站点的位图页面上的建议使用 AsynchTask 加载图像。

一切都完美而顺利,但我有一个问题。当适配器中的 View 被回收时,它仍然有对旧图像(ImageView)的引用。如果用户滚动缓慢,则 AsynchTask 有足够的时间来加载新图像并显示它,因此用户看不到新图像的重新加载。

但是,如果用户滚动得很快,加载图像的延迟意味着他们在被新图像替换之前看到旧图像(当视图被另一个项目使用时加载)。

所以,我的问题是,我怎样才能检测到 View 何时在屏幕上不再可见,然后我可以删除图像?这意味着用户会看到一个空的列表视图项目,该项目最终将加载适当的图像,这样看起来会更好。

提前谢谢了。这是列表视图适配器代码。

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    View view = convertView;
    ListViewHolder viewHolder;

    // if this is not a recycled view then create a new view for the data...
    if (view == null)
    {
        view = this.inflater.inflate(R.layout.target_list_view_layout, null, true);

        viewHolder = new ListViewHolder();

        viewHolder.manufacturer = (TextView) view.findViewById(R.id.manufacturer);
        viewHolder.targetName = (TextView) view.findViewById(R.id.targetName);
        viewHolder.targetThumbnail = (ImageView) view.findViewById(R.id.targetThumbnail);

        view.setTag(viewHolder);
    } else
    {
        viewHolder = (ListViewHolder) convertView.getTag();
    }

    TargetDescriptor targetDescriptor = this.selectedTargets.get(position);

    viewHolder.manufacturer.setText(targetDescriptor.manufacturer);
    viewHolder.targetName.setText(targetDescriptor.targetName);

    // At this point I pass the image view reference to my background task to load the image
    LoadImageViewAsynchTask loadImageTask = new LoadImageViewAsynchTask(viewHolder.targetThumbnail, targetDescriptor);
    loadImageTask.execute(new Integer[]
    { 64, 64 });

    return view;
}

编辑:如果有帮助,那些使用 eBay Android 应用程序的人可以看到我正在寻找的效果。

4

4 回答 4

28

实际上有一种更简单的方法可以做到这一点,您可以使用监听器:

mListView.setRecyclerListener(new AbsListView.RecyclerListener() {
@Override
    public void onMovedToScrapHeap(View view) {
  }
});
于 2014-10-23T13:43:22.237 回答
3

不敢相信我有多愚蠢,这是一个非常容易解决的问题!

基本上在我的适配器中,如果视图被重新循环,我只需要在重新使用视图之前将 ImageView 位图设为空。这意味着在加载下一个图像之前,列表视图项中的图像是空白的。在后台加载新图像时,我不再遇到旧图像存在的问题。

我还对适配器进行了更改,取消了绑定到视图的任何当前 AsynchTask,该视图可能仍在加载不再需要的先前图像。当我在列表视图中快速滚动时,这种情况非常普遍,通常会备份两个或更多图像加载,因此图像会在确定正确图像之前更改几次。

这是新代码,其中的更改已注释,因此您可以查看发生了什么:

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    ListViewHolder viewHolder;

    // if this is not a recycled view then create a new view for the data...
    if (convertView == null)
    {
        convertView = this.inflater.inflate(R.layout.target_list_view_layout, null, true);

        viewHolder = new ListViewHolder();

        viewHolder.manufacturer = (TextView) convertView.findViewById(R.id.manufacturer);
        viewHolder.targetName = (TextView) convertView.findViewById(R.id.targetName);
        viewHolder.targetThumbnail = (ImageView) convertView.findViewById(R.id.targetThumbnail);

        convertView.setTag(viewHolder);
    } else
    {
        viewHolder = (ListViewHolder) convertView.getTag();

        // Cancel the previous attempt to load an image as this is going to be superceded by the next image
        viewHolder.loadImageViewAsynchTask.cancel(true);

        // Clear down the old image so when this view is displayed, the user does not see the old image before the
        // new image has a chance to load in the background
        viewHolder.targetThumbnail.setImageBitmap(null);
    }

    TargetDescriptor targetDescriptor = this.selectedTargets.get(position);

    viewHolder.manufacturer.setText(targetDescriptor.manufacturer);
    viewHolder.targetName.setText(targetDescriptor.targetName);

    LoadImageViewAsynchTask loadImageViewAsynchTask = new LoadImageViewAsynchTask(viewHolder.targetThumbnail);
    loadImageViewAsynchTask.setTargetDescriptor(targetDescriptor);
    loadImageViewAsynchTask.execute(new Integer[]
    { 64, 64 });

    // Keep a reference to the task so we can cancel it if the view is recycled next time round to prevent
    // un-neccessary image loads that are out of date
    viewHolder.loadImageViewAsynchTask = loadImageViewAsynchTask;

    return convertView;
}
于 2013-10-27T23:24:17.167 回答
0

如果您为位图使用 LruCache 并且让列表视图中的每个视图保留 LruCache 项的密钥,您可以更好地控制它。然后,在 getView 中,您首先尝试从 LruCache 获取位图,然后再次下载如果它不存在。

另外,您可以在 ListView 上设置一个 setOnScrollListener(),并在用户第一次滚动时使用它的 visibleItemCount 参数来调整 LruCache 的大小。

于 2013-10-27T19:18:25.090 回答
-1

如果您使用覆盖getView方法来显示图像,那么您可以检查 convertView。如果它为null,那么您可以发现它已回收或之前未初始化。就像这样:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    ViewHolder viewholder;
    if (convertView == null) {
        viewholder = new ViewHolder();
        inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.item_row, parent, false);
        viewholder.tvID = (TextView) convertView.findViewById(R.id.tvID);
        viewholder.tvName = (TextView) convertView.findViewById(R.id.tvName);
        viewholder.tvFamily = (TextView) convertView.findViewById(R.id.tvFamily);
        viewholder.ivMain = (ImageView) convertView.findViewById(R.id.ivMain);
    } else {
        viewholder = (ViewHolder) convertView.getTag();
    }
    viewholder.tvID.setText(IDs[position]);
    viewholder.tvName.setText(Names[position]);
    viewholder.tvFamily.setText(Familys[position]);
    viewholder.ivMain.setImageResource(Images[position]);
    convertView.setTag(viewholder);
    return convertView;
}
于 2013-10-27T18:13:20.247 回答