Android ListView обновляет миниатюры изображений с помощью AsyncTask. Причины Просмотр повторного использования

Я пытался заставить миниатюры работать с AsyncTask для файлов изображений в ListView. У меня была общая проблема с переработкой строк, и поэтому при прокрутке миниатюры назначаются неправильным строкам. Я попытался добавить теги в ImageView, а затем подтвердить теги в onPostExecute() в AsyncTask, но мои попытки не увенчались успехом. Кто-нибудь, пожалуйста, помогите!

Пользовательский адаптер выглядит следующим образом:

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;
    }
}

Я до сих пор не могу понять, что еще нужно сделать, чтобы миниатюры отображались в правильных строках ВО ВРЕМЯ ПРОКРУТКИ.

Обратите внимание: после прокрутки от затронутых строк и прокрутки вниз все работает нормально, но проблема с прокруткой не исчезает!


person Daksh    schedule 27.07.2012    source источник
comment
Дубликат stackoverflow.com/q/6081497/403455, stackoverflow.com/q/9832944/403455   -  person Jeff Axelrod    schedule 28.07.2012


Ответы (3)


Из этой записи в блоге:

То, что вы хотите сделать, это запустить все операции ввода-вывода для каждой строки или любую тяжелую подпрограмму, связанную с процессором, асинхронно в отдельном потоке. Хитрость заключается в том, чтобы сделать это и по-прежнему соблюдать поведение повторного использования 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;
}
person Jeff Axelrod    schedule 28.07.2012
comment
хорошо спасибо большое! метод ViewHolder теперь работал отлично. Должно быть, я раньше делал что-то не так! но теперь главная проблема заключается в том, что когда я прокручиваю и создаются растровые изображения, они не назначаются соответствующей строке, если я не прокручиваю строку с экрана, а затем снова возвращаюсь! есть ли какой-либо метод, с помощью которого я мог бы опросить представление и назначить растровые изображения, используя HashMap имени файла и его растровое изображение, созданное в AsyncTask? ваша помощь была бы очень признательна мне! еще раз спасибо! - person Daksh; 29.07.2012
comment
Я не уверен, что могло вызвать эту новую проблему; Я предлагаю вам опубликовать новый вопрос об этом. Но я нашел лучшую реализацию, которую вы могли бы использовать, из Блог разработчиков Android. Полный демонстрационный проект находится здесь. - person Jeff Axelrod; 29.07.2012
comment
Джефф, не могли бы вы опубликовать всю функцию getView()? Я пытаюсь реализовать это, и обновляется только первый элемент ListView, похоже, у меня проблемы с повторным использованием представлений. - person steve_patrick; 31.10.2013
comment
Нужно ли перерабатывать/утилизировать растровое изображение, если оно не является правильным mPosition в onPostExecute? - person Matthias; 13.05.2014
comment
что такое курсор? и какой возврат? - person ; 07.03.2016

Использование AsyncTask.THREAD_POOL_EXECUTOR приведет к запуску нескольких потоков для получения миниатюр. Нет никакого порядка в том, как они запускаются или заканчиваются и устанавливают изображение для представления. При прокрутке назад и вперед вы можете получить неправильные изображения в своих представлениях.

Я протестировал AsyncTask.THREAD_POOL_EXECUTOR, и это приводит к неправильному изображению для некоторых представлений.

person user1439970    schedule 26.06.2013

вы можете попробовать передать ListView в свой конструктор адаптера из своей деятельности (может быть, даже в вашу асинхронную задачу зависит от того, как вы хотите ее реализовать). и сравните позицию, используя getFirstVisiblePosition() и getLastVisiblePosition(), с вашим mPosition

person Pete    schedule 18.01.2013