【问题标题】:Android ListView updating of Image Thumbnails using AsyncTask Causes View recyclingAndroid ListView 使用 AsyncTask 更新 Image Thumbnails 导致 View 回收
【发布时间】:2012-07-26 14:34:03
【问题描述】:

我一直在尝试让缩略图与 ListView 中的图像文件的 AsyncTask 一起使用。 我一直遇到行回收的常见问题,因此在滚动缩略图时被分配到错误的行。我曾尝试向 ImageView 添加标签,然后在 AsyncTask 的 onPostExecute() 中确认标签,但我的尝试没有成功。有人请帮忙!

自定义适配器如下:

public class MySimpleAdapter extends SimpleAdapter {

        public MySimpleAdapter(Context context,
                List<? extends Map<String, ?>> data, int resource,
                String[] from, int[] to) {
            super(context, data, resource, from, to);
            // TODO Auto-generated constructor stub
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            final View v = super.getView(position, convertView, parent);
            thumbnail = (ImageView) v.findViewById(R.id.thumbnail);
            checker = (CheckBox) v.findViewById(R.id.checkBox);
            filenametext = (TextView) v.findViewById(R.id.text1);
            String pathtoimage = startLocation.concat("/"
                    + filenametext.getText().toString());
            desctext = (TextView) v.findViewById(R.id.text2);
            String temp = desctext.getText().toString();
            if (temp.equals("Directory") == true) {
                checker.setEnabled(false);
                switch (theme) {
                case 0:
                    thumbnail.setImageResource(R.drawable.folder_light);
                    break;
                case 1:
                    thumbnail.setImageResource(R.drawable.folder_dark);
                    break;
                }

            } else {
                checker.setEnabled(true);
                if (filenametext.getText().toString().endsWith(".jpg")
                        || filenametext.getText().toString().endsWith(".png")
                        || filenametext.getText().toString().endsWith(".bmp")) {
                    Boolean hashmapfound = false;
                    for (HashMap.Entry<String, Bitmap> entry : thumbnaillist
                            .entrySet()) {
                        String key = entry.getKey();
                        if (key.equals(filenametext.getText().toString())) {
                            Log.d(TAG, "HashMapKey Found!");
                            thumbnail.setImageBitmap(entry.getValue());
                            hashmapfound = true;
                        }
                    }
                    if (!hashmapfound) {
                        Log.d(TAG, "NO HashMapKey Found! Adding to HashMap!");
                        switch (theme) {
                        case 0:
                            thumbnail
                                    .setImageResource(R.drawable.unknown_image_light);
                            break;
                        case 1:
                            thumbnail
                                    .setImageResource(R.drawable.unknown_image_dark);
                            break;
                        }
                        thumbnail.setTag((filenametext.getText().toString()));
                        new GetBitMaps(thumbnail).execute(pathtoimage,
                                filenametext.getText().toString());
                    }

                } else {
                    switch (theme) {
                    case 0:
                        thumbnail.setImageResource(R.drawable.file_light);
                        break;
                    case 1:
                        thumbnail.setImageResource(R.drawable.file_dark);
                        break;
                    }
                }
            }
            return v;
        }

    }

AsyncTask 如下:

class GetBitMaps extends AsyncTask<String, Void, Bitmap> {

    private ImageView thumbnail;
    private String imageName;

    public GetBitMaps(ImageView thumb) {
        // TODO Auto-generated constructor stub
        thumbnail = thumb;
    }

    @Override
    protected Bitmap doInBackground(String... pathtoimage) {
        Bitmap bmp = createThumbnail(pathtoimage[0]);
        thumbnaillist.put(pathtoimage[1], bmp);
        imageName = pathtoimage[1];
        return bmp;
    }

    @Override
    protected void onPostExecute(Bitmap bmp) {

        if (((String) thumbnail.getTag()).equals(imageName)) {
            thumbnail.setImageBitmap(bmp);
        }
    }

    private Bitmap createThumbnail(String filepath) {
        Bitmap bmp = BitmapFactory.decodeFile(filepath);
        int w = bmp.getWidth();
        int h = bmp.getHeight();
        if (w > h) {
            w = 80;
            h = 40;
        } else if (w < h) {
            w = 40;
            h = 80;
        }
        bmp = Bitmap.createScaledBitmap(bmp, w, h, true);

        return bmp;
    }
}

我仍然不知道还需要做什么才能让缩略图在滚动时显示在正确的行上。

请注意:滚动离开受影响的行并向下滚动后,一切正常,但滚动问题并没有消失!

【问题讨论】:

标签: android listview android-asynctask thumbnails recycle


【解决方案1】:

来自this blog post

您想要做的是在单独的线程中异步运行所有每行 IO 或任何繁重的 CPU 绑定例程。这里的诀窍是这样做并且仍然遵守 ListView 的回收行为。例如,如果您运行 AsyncTask 以在适配器的 getView() 中加载个人资料图片,则您为其加载图像的视图可能会在 AsyncTask 完成之前被回收到另一个位置。因此,您需要一种机制来了解在您完成异步操作后视图是否还没有被回收。

实现此目的的一个简单方法是将一些信息附加到视图中,以标识与其关联的行。然后您可以检查异步操作完成时视图的目标行是否仍然相同。有很多方法可以实现这一点。这只是您可以做到的一种方法的简单草图:

public View getView(int position, View convertView,
        ViewGroup parent) {
    ViewHolder holder;

    ...

    holder.position = position;

    new ThumbnailTask(position, holder)
            .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);

    return convertView;
}

private static class ThumbnailTask extends AsyncTask {
    private int mPosition;
    private ViewHolder mHolder;

    public ThumbnailTask(int position, ViewHolder holder) {
        mPosition = position;
        mHolder = holder;
    }

    @Override
    protected Cursor doInBackground(Void... arg0) {
        // Download bitmap here
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (mHolder.position == mPosition) {
            mHolder.thumbnail.setImageBitmap(bitmap);
        }
    }
}

private static class ViewHolder {
    public ImageView thumbnail;
    public int position;
}

【讨论】:

  • 好的,非常感谢! ViewHolder 方法现在完美运行。我之前一定是做错了什么!但现在主要担心的是,当我滚动并创建位图时,它们不会被分配到相应的行,除非我将该行滚动到屏幕之外然后再次返回!是否有任何方法可以轮询视图并使用文件名的 HashMap 及其在 AsyncTask 中创建的位图分配位图?我将非常感谢您的帮助!再次感谢!
  • 我不确定是什么导致了这个新问题;我建议你发布一个关于它的新问题。但是,我确实找到了一个更好的实现,您可以从Android Developers Blog 使用它。完整的演示项目是here
  • 杰夫,你能把整个 getView() 函数贴出来吗?我正在尝试实现它,只有 ListView 的第一个元素得到更新,看起来我在重用视图时遇到了问题
  • 如果位图在 onPostExecute 中的 mPosition 不正确,您是否必须回收/处置位图?
  • 什么是光标?什么回报?
【解决方案2】:

使用 AsyncTask.THREAD_POOL_EXECUTOR 最终会运行多个线程来获取缩略图。 他们运行或完成并为视图设置图像的方式没有顺序。来回滚动时,您最终可能会在视图中看到错误的图像。

我测试了 AsyncTask.THREAD_POOL_EXECUTOR,它确实会导致某些视图出现错误的图像。

【讨论】:

    【解决方案3】:

    您可以尝试将 ListView 从您的 Activity 传递到您的 Adapter 构造函数中(甚至可能进入您的异步任务,这取决于您希望如何实现它)。 并使用 getFirstVisiblePosition() 和 getLastVisiblePosition() 将位置与您的mPosition

    进行比较

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-09-17
      • 1970-01-01
      • 2013-10-28
      • 2018-05-12
      • 1970-01-01
      • 2011-07-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多