6

Я пытаюсь отобразить список песен, найденных на устройстве, запрашивая данные непосредственно из MediaStore. Я использую RecyclerView и адаптер, который использует CursorAdapter для получения данных из MediaStore. Когда вызывается адаптер onBindViewHolder, запрос передается функции bindViewCursorAdapter, все визуальные элементы установлены.Изображение мерцает при прокрутке в RecyclerView

public class ListRecyclerAdapter3 extends RecyclerView.Adapter<ListRecyclerAdapter3.SongViewHolder> { 

    // PATCH: Because RecyclerView.Adapter in its current form doesn't natively support 
    // cursors, we "wrap" a CursorAdapter that will do all teh job 
    // for us 
    public MediaStoreHelper mediaStoreHelper; 
    CustomCursorAdapter mCursorAdapter; 
    Context mContext; 


    public class SongViewHolder extends RecyclerView.ViewHolder { 

     public TextView textItemTitle; 
     public TextView textItemSub; 
     public ImageView imgArt; 

     public int position; 
     public String album_id; 
     public String path_art; 
     public String path_file; 

     public SongViewHolder(View v) { 
      super(v); 
      textItemTitle = (TextView) v.findViewById(R.id.textItemTitle); 
      textItemSub = (TextView) v.findViewById(R.id.textItemSub); 
      imgArt = (ImageView) v.findViewById(R.id.imgArt); 
     } 
    } 

    private class CustomCursorAdapter extends CursorAdapter { 

     public CustomCursorAdapter(Context context, Cursor c, int flags) { 
      super(context, c, flags); 
     } 

     @Override 
     public View newView(final Context context, Cursor cursor, ViewGroup parent) { 

      View v = LayoutInflater.from(parent.getContext()) 
        .inflate(R.layout.song_item, parent, false); 

      final SongViewHolder holder = new SongViewHolder(v); 

      v.setTag(holder); 

      return v; 
     } 

     @Override 
     public void bindView(View view, Context context, Cursor cursor) { 
      SongViewHolder holder = (SongViewHolder) view.getTag(); 

      holder.position = cursor.getPosition(); 

      holder.textItemTitle.setText(cursor.getString(cursor.getColumnIndex("title"))); 
      holder.textItemSub.setText(cursor.getString(cursor.getColumnIndex("artist"))); 

      holder.album_id = cursor.getString(cursor.getColumnIndex("album_id")); 
      holder.path_file = cursor.getString(cursor.getColumnIndex("_data")); 

      Picasso.with(holder.imgArt.getContext()) 
        .cancelRequest(holder.imgArt); 
      holder.imgArt.setImageDrawable(null); 

      new DownloadImageTask(mediaStoreHelper, context, holder.imgArt).execute(holder.album_id); 
     } 

    } 

    private class DownloadImageTask extends AsyncTask<String, String, String> { 

     private MediaStoreHelper mediaStoreHelper; 
     private ImageView imageView; 
     private Context context; 

     public DownloadImageTask(MediaStoreHelper mediaStoreHelper, Context context, ImageView imageView) 
     { 
      this.mediaStoreHelper = mediaStoreHelper; 
      this.context = context; 
      this.imageView = imageView; 
     } 
     @Override 
     protected String doInBackground(String... ids) { 
      return mediaStoreHelper.getAlbumArtPath(ids[0]); 
     } 

     protected void onPostExecute(String result) { 
      Picasso.with(context) 
        .load(new File(result)) 
        .placeholder(R.drawable.ic_music) 
        .fit() 
        .into(imageView); 
     } 
    } 

    @Override 
    public void onBindViewHolder(SongViewHolder holder, int position) { 
     // Passing the binding operation to cursor loader 
     mCursorAdapter.getCursor().moveToPosition(position); 
     mCursorAdapter.bindView(holder.itemView, mContext, mCursorAdapter.getCursor()); 
    } 

    @Override 
    public SongViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
     // Passing the inflater job to the cursor-adapter 
     View v = mCursorAdapter.newView(mContext, mCursorAdapter.getCursor(), parent); 
     return new SongViewHolder(v); 
    } 
} 

Проблемная часть загрузки изображения с состоит из двух частей:

  • С ALBUMID я получил от Cursor, мне нужно использовать ContentResolver, чтобы получить путь альбома файлов искусства
  • Загрузите изображение в поле ImageView, используя путь файла

T hese два прохода необходимо сделать в фоновом режиме, иначе прокрутка станет очень лаги. В функции bindView я называюсь AsyncTask делает работу, но проблема заключается в том, что при прокрутке быстро, несколько запросов изображений разработаны и это результат:

enter image description here

Как вы можете видеть из кода Я пытался отменить ожидающие запросы Пикассо по конкретному ImageView, но этого недостаточно. Можно ли устранить эту проблему?

+1

Я думаю, что вы можете включить прослушиватель Scroll в RecyclerView и только начать загрузку изображения, когда он прекратил прокрутку. – X3Btel

+0

@ X3Btel хорошая мысль, я попробую – fillobotto

ответ

4

я решена путем добавления поля в ViewHolder содержащий AsyncTask относительно этого элемента. В функции bindView я установил AsyncTask.cancel(true), и внутри задачи я сделал чек isCancelled() перед тем, как применить полученное изображение, используя Picasso.with(...).load(...). Это само по себе мерцало.

BindView

if(holder.downloadImageTask != null) 
     holder.downloadImageTask.cancel(true); 

     holder.downloadImageTask = (DownloadImageTask) new DownloadImageTask(mediaStoreHelper, context, holder.imgArt).execute(holder.album_id); 

AsyncTask

private class DownloadImageTask extends AsyncTask<String, String, String> { 

     private MediaStoreHelper mediaStoreHelper; 
     private ImageView imageView; 
     private Context context; 

     public DownloadImageTask(MediaStoreHelper mediaStoreHelper, Context context, ImageView imageView) 
     { 
      this.mediaStoreHelper = mediaStoreHelper; 
      this.context = context; 
      this.imageView = imageView; 
     } 
     @Override 
     protected String doInBackground(String... ids) { 
      return mediaStoreHelper.getAlbumArtPath(ids[0]); 
     } 

     protected void onPostExecute(String result) { 
      if(!isCancelled()) 
       Picasso.with(context) 
         .load(new File(result)) 
         .placeholder(R.drawable.ic_music) 
         .fit() 
         .into(imageView); 
     } 
    } 

Ради полноты картины, это также удалить изображение из переработанных элементов и установить заполнитель.

@Override 
public void onViewRecycled(SongViewHolder holder) { 
    super.onViewRecycled(holder); 
    Picasso.with(holder.itemView.getContext()) 
      .cancelRequest(holder.imgArt); 
    Picasso.with(holder.itemView.getContext()) 
      .load(R.drawable.ic_music) 
      .fit() 
      .into(holder.imgArt); 
} 

Это решение заставило меня думать, что проблема была количество времени intercurring внутри AsyncTask между извлечения изображений из MediaStore и времени, когда изображение фактически применяется в ImageView от Пикассо.

+0

просто 'onViewRecycled' достаточно, чтобы решить проблему, не нужно менять асинхронную задачу –

0

Комментарий эти линии

Мерцание происходит потому, что привязка не вызывать только один раз для одного элемента, так что звонить снова и снова для одной строки и вы устанавливаете нуль каждый рабочий день, а также установка вид на него. производя мерцание.

Picasso.with(holder.imgArt.getContext()) 
        .cancelRequest(holder.imgArt); 
      holder.imgArt.setImageDrawable(null); 
+1

К сожалению, это не решение. Мерцание происходит из-за нескольких запросов по одному и тому же ImageView, а не потому, что я устанавливаю значение null. – fillobotto

+0

мерцание происходит потому, что bind не является вызовом только один раз для одного элемента, поэтому он вызывает снова и снова для одной строки, и вы каждый раз устанавливаете значение null, а также устанавливаете вид на него. производя мерцание. –

+0

Первая часть смысла права, вторая - нет. Кстати, я попробовал, как вы предложили, и результат точно такой же. – fillobotto

0

Асинхронная задача, которую вы используете, является абсолютно неправильной.

Во-первых, вы никогда не должны выполнять анонимную асинхронную задачу.

Во-вторых, избавитесь от этой асинхронной задачи в адаптере, потому что это, по-видимому, вызывает проблему.

Получить данные для адаптера, а затем показать его.

Btv, прекращение Picasso лучше сделать так:

 @Override public void onViewRecycled(VH holder) { 
      super.onViewRecycled(holder); 
      Picasso.with(holder.itemView.getContext()) 
        .cancelRequest(holder.getImageView()); 
     } 
+1

Ну, я использую AsyncTask для извлечения изображения из MediaStore. Этот процесс, кажется, занимает некоторое время и делает прокрутку очень медленным – fillobotto