ANDROID: загрузка асинхронных изображений в listView - PullRequest
3 голосов
/ 23 марта 2012

Я хотел бы отобразить ListView с настроенным адаптером (с изображением и текстом).

Изображения загружаются с удаленных серверов, поэтому я решил использовать AsyncTask.

На самом деле, изображения хорошо отображаются, но если я быстро прокручиваю вниз, в течение 1/2 секунды отображается неправильное изображение (после загрузки появляется правильное изображение)

Вот код моего адаптера:

public class GiAdapter extends BaseAdapter {

    private Context mContext;

    private List<SiteStaff> mListAppInfo;
    private HashMap<Integer, ImageView> views;
    private HashMap<String,Bitmap> oldPicts = new  HashMap<String,Bitmap>();
    private LayoutInflater mInflater;
    private boolean auto;

    private final String BUNDLE_URL = "url";
    private final String BUNDLE_BM = "bm";
    private final String BUNDLE_POS = "pos";
    private final String BUNDLE_ID = "id";

    public GiAdapter(Context context, List<SiteStaff> list) {
        mContext = context;
        mListAppInfo = list;
        views = new HashMap<Integer, ImageView>();
        mInflater = LayoutInflater.from(mContext);

    }

    @Override
    public int getCount() {
        return mListAppInfo.size();
    }

    @Override
    public Object getItem(int position) {
        return mListAppInfo.get(position).getId();
    }

    @Override
    public long getItemId(int position) {
        return mListAppInfo.get(position).getId();
    }

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

        LinearLayout layoutItem;

        // reuse of convertView
        if (convertView == null) {
            layoutItem = (LinearLayout) mInflater.inflate(R.layout.auto_gi, parent, false);
        } else {
            layoutItem = (LinearLayout) convertView;
        }

        // infos for the current element
        SiteStaff entry = mListAppInfo.get(position);

        //set some text fields
        TextView name = (TextView) layoutItem.findViewById(R.id.name);
        TextView size = (TextView) layoutItem.findViewById(R.id.size); 

        name.setText(entry.getName());
        size.setText(entry.getSize());

        // get the imageView for the current object
        ImageView v = (ImageView) layoutItem.findViewById(R.id.gi_image);

        // put infos in bundle and send to the LoadImage class
        Bundle b = new Bundle();

        //url of the pict
        b.putString(BUNDLE_URL, entry.getUrl());

        //position in the listView
        b.putInt(BUNDLE_POS, position);

        //id of the current object
        b.putInt(BUNDLE_ID, entry.getId());

        //put info in the map in order to display in the onPostExecute
        views.put(position, v);

        // thread
        new LoadImage().execute(b);

        return layoutItem;

    }

    //asyncTackClass for loadingpictures
    private class LoadImage extends AsyncTask<Bundle, Void, Bundle> {

        @Override
        protected Bundle doInBackground(Bundle... b) {

            Bitmap bm =null;

            //cache: for better performance, check if url alredy exists
            if(oldPicts.get(b[0].getString(BUNDLE_URL))==null){
                bm = Utils.getBitMapFromUrl(b[0].getString(BUNDLE_URL));
                oldPicts.put(b[0].getString(BUNDLE_URL),bm);
            }else{
                bm = oldPicts.get(b[0].getString(BUNDLE_URL));
            }

            // get info from bundle
            Bundle bundle = new Bundle();
            bundle.putParcelable(BUNDLE_BM, bm);
            bundle.putInt(BUNDLE_POS, b[0].getInt(BUNDLE_POS));

            return bundle;

        }

        @Override
        protected void onPostExecute(Bundle result) {
            super.onPostExecute(result);

            //get picture saved in the map + set
            ImageView view = views.get(result.getInt(BUNDLE_POS));
            Bitmap bm = (Bitmap) result.getParcelable(BUNDLE_BM);

            if (bm != null){ //if bitmap exists...
                view.setImageBitmap(bm);
            }else{ //if not picture, display the default ressource
                view.setImageResource(R.drawable.unknow);
            }

        }

    }

}

Спасибо!

Ответы [ 3 ]

6 голосов
/ 19 апреля 2012

Как упоминалось Shubhayu, вы видите неправильные изображения в строках ListView, потому что ваш адаптер перерабатывает представление для создания каждой строки, а ваши асинхронные задачи поддерживают ссылки на это переработанное представление.Когда ваши задачи будут выполнены, они обновят ImageView, который может на самом деле соответствовать какой-то другой строке к этому моменту.

Проблема с раздуванием нового представления при каждом вызове getView заключается в том, что вы в конечном итоге получите плохой результат.производительность для вашего ListView при прокрутке.Вы (правильно) используете convertView, чтобы уменьшить эти издержки, вам просто не хватает части правильной привязки ImageView к Async.Я нашел решение здесь: http://android -developers.blogspot.com / 2010/07 / multithreading-for-performance.html

Таким образом, это в основном расширение Drawable для инкапсуляциизадача, и только обновление изображения, если задача соответствующим образом привязана к изображению.Раздел «Управление параллелизмом» посвящен самой проблеме, с которой вы столкнулись.В частности, прочитайте абзац прямо перед этим разделом, чтобы получить сводную информацию о причинах вашей проблемы.

5 голосов
/ 23 марта 2012

Это случилось со мной тоже. Я понял, что до того, как новое изображение будет связано с изображением, какое-то время отображалось старое. В вашем случае происходит то, что ваша правильная информация показывает onPostExecute (), что занимает некоторое время, и до того, как показываются все данные, которые были установлены в convertview. Есть 2 способа, которыми вы в основном можете это исправить,

В вашем getView () измените блок if {} ... else {} на

layoutItem = (LinearLayout) mInflater.inflate(R.layout.auto_gi, parent, false);

или

установить вид изображения с некоторым заполнителем в getView () до вызова onPostExecute ().

Надеюсь, это поможет.

1 голос
/ 23 марта 2012

Когда вы расширяете BaseAdapter и внедряете getView (int position, View convertView, ViewGroup parent), convertView на самом деле является переработанным представлением.

Это вид, который был уничтожен (последний вид, который будет удален с экрана). Что вы можете сделать, так это очистить convertView, чтобы он не показывал устаревшие данные. возможно найдите вид с изображением и сделайте layoutItem.removeAll (). Надеемся, что это очистит представления и даст вам чистую компоновку для работы.

...