The application uses recyclerwiew to display a list of contacts. In the old version of the application, 300mb photos of users rocked once and for all their lives and then they simply got to the OnBindViewHolder from the database. No problems in the display (in recyclerview) did not arise.

Now the photos are downloaded from the network as needed (if they were not previously recorded in the database), their display (asynchronously, of course) is recorded in the database and the result is displayed in the desired item. Everything looks great, but as soon as the list of contacts scroll, their photos are duplicated and only when the scroll stops, they disappear, they are re-created correctly. That is, recyclerview at the time of the stop flashes like a Christmas tree. It is clear that this is due to the fact that recyclerview reuses visible itemview. The problem is solved if you switch from recyclerview to listview and be sure to set it to setHasFixedSize (true). However, I just need to use recyclerview, not a listview.

Adapter code:

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

Now in detail about the new method holder.mPhoto.setEmploeePhoto, which just either shows the image from the database, or downloads from the network, saves it to the database and shows it.

 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(); } } } 
  • or you can show the code onBindViewHolder - miha_dev
  • @miha_dev added code and comments inside it, look, please - Vadim Kravchenko
  • one
    Try to nullify the ImageView image before adding a new image to it - mImageView.setImageDrawable(null) . - Vadim
  • @ Vadim crash in display began to occur a little less, but this did not solve the problem = ( - Vadim Kravchenko
  • How many times is the onBindViewHolder method called on each view? One? - Vadim

1 answer 1

You need to override the getItemId(int position) method in the adapter.

 public long getItemId(int position) { return position; } 

After this, indicate to the adapter that it has its own ID for each cell as follows:

 adapter.setHasStableIds(true); 

Now cells will not be reused

  • Override this method by specifying your list of objects. I specified immediately after adapter initialization setHasStableIds (true); But this did not solve the problem = (Only holder.setIsRecyclable (false) helps me; but this is a bad solution because it violates the RecyclerView concept and slows down the scroll. - Vadim Kravchenko
  • @VadimKravchenko, try to display pictures not by a handwriting method, but through Picasso / Glide type - YuriySPb
  • I tried both of these libraries, but also did not give any results = (And, I have two different projects with the same problem (one was written not by me, and the second, respectively, by me with a standard Recycler Adapter and a standard View Holder), and I tried everything on each that I was advised in the comments. - Vadim Kravchenko