@powercot

Как запретить RecyclerView переиспользование элементов списка?

В приложении используется recyclerwiew для отображения списка контактов. В старой версии приложения один раз и на всю жизнь качались 300мб фотографий пользователей и потом в OnBindViewHolder просто доставались из базы данных. Никаких проблем в отображении (в recyclerview) не возникало.
Сейчас же фотографии скачиваются из сети по мере необходимости их отображения (естественно асинхронно), записываются в БД и результат отображается в нужном айтеме. Всё выглядит прекрасно, но как только происходит scroll списка контактов, их фотографии дублируются и только когда scroll остановится, они исчезают, пересоздаются уже корректно отображаются. То есть recyclerview в момент остановки мигает как новогодняя ёлка. Понятно, что это происходит от того, что recyclerview переиспользует видимые itemview. Проблема решается, если перейти с recyclerview на listview и обязательно установить ему setHasFixedSize(true). Однако, мне просто необходимо использовать recyclerview, а не listview.

Код адаптера:

@Override
public void onBindViewHolderCursor(ContactViewHolder holder, Cursor cursor) {

    String firstName = cursor.getString(cursor.getColumnIndex(DatabaseSchemas.Profile.FIRST_NAME));
    String lastName = cursor.getString(cursor.getColumnIndex(DatabaseSchemas.Profile.LAST_NAME));
    holder.mName.setText(String.format("%s %s", lastName, firstName));

    long profileId = cursor.getInt(cursor.getColumnIndex(DatabaseSchemas.Profile.ID));
    boolean gender = cursor.getString(cursor.getColumnIndex(DatabaseSchemas.Profile.SEX)).equals("M");
    holder.mPhoto.setEmploeePhoto(mContext, profileId, gender, holder.mPhoto);
    //ВНИМАНИЕ ниже старый метод отображения изображений, который работает без проблем
    //ImageUtils.displayUserPhoto(mContext, profileId, gender, holder.mPhoto);

    holder.setHighlightQuery(mQuery);
}


Теперь подробно о новом методе holder.mPhoto.setEmploeePhoto, который как раз либо показывает изображение из базы данных, либо качает из сети, сохраняет в БД и показывает.

public void setEmploeePhoto(final Context context, final long mEmployee_id, final boolean gender, final ImageView imageView) {

    if (ImageUtils.userPhotoIsExist(context, mEmployee_id)){

        ImageUtils.displayUserPhoto(context, mEmployee_id, gender, imageView);
//ВНИМАНИЕ, это тот самый метод отображения фото, 
//который был закомментирован в OnBindViewHolder, 
//и ещё ниже он снова используется уже после окончания загрузки и записи в БД. 
//Но только пока он работал в OnBindViewHolder, фото прекрасно отображались при скроллинге, 
//а вот здесь появляется проблема с дублированием и перемигиванием новогодней ёлкой

    }
    else {

        JSONObject filter = new JSONObject();
        JSONArray values = new JSONArray();
        values.put(String.valueOf(mEmployee_id));
        try {
            filter.put("values", values);
            filter.put("field", "user_id");
        } catch (JSONException e) {
            e.printStackTrace();
            return;
        }
        new SapApi(getContext()).getPhoto(filter)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<InputStream>() {
                    @Override
                    public void call(final InputStream inputStream) {
//Если фото успешно скачалось из сети
                        new LoadAndWritePhoto(context,mEmployee_id, gender, imageView, inputStream).execute();

                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Toast.makeText(getContext(), throwable.getMessage(), Toast.LENGTH_LONG).show();
                    }
                });

    }

}

public class LoadAndWritePhoto extends AsyncTask<Void, Void, Void> {//Потому что нельзя записывать данные в БД в главном потоке
    Context mmContext;
    long profileId;
    boolean gender;
    ImageView mImageView;
    InputStream inputStream;

    public LoadAndWritePhoto(Context mmContext, long profileId, boolean gender, ImageView mImageView, InputStream inputStream) {
        this.mmContext = mmContext;
        this.profileId = profileId;
        this.gender = gender;
        this.mImageView = mImageView;
        this.inputStream = inputStream;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... params) {//фоновая логика
        {
            try {
                PhotosSdCardWriter.writePhotosFromJson(mmContext, inputStream);
            } catch (Exception e){
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    protected void onProgressUpdate(Void... values) {//обновление интерфейса до окончания выполнения потока (не требуется)
        super.onProgressUpdate(values);
    }

    @Override
    protected void onPostExecute(Void aVoid) {//результат фоновой логики
        super.onPostExecute(aVoid);
        try {
            ImageUtils.displayUserPhoto(mmContext, profileId, gender, mImageView);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • Вопрос задан
  • 309 просмотров
Пригласить эксперта
Ответы на вопрос 1
zagayevskiy
@zagayevskiy Куратор тега Android
Android developer at Yandex
Это в корне неверный подход. Смысл ресайклера именно в переиспользовании. Вам нужно изменить способ обращения с картинками, у вас оно написано неправильно. Возьмите любую популярную библиотеку типа Glide или Picasso - с их использованием таких проблем не будет.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы