2016-07-28 128 views
6

我试图显示设备上找到的歌曲列表直接从MediaStore请求数据。我正在使用RecyclerView和使用CursorAdapter从MediaStore获取数据的适配器。 当调用适配器的onBindViewHolder时,请求被传递给CursorAdapterbindView函数,设置所有可视元素。图片闪烁时滚动在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这两段话需要在后台完成,否则滚动将变得非常迟缓。在bindView功能我叫AsyncTask的做工作,但问题是,在滚动快,多种图像请求阐述,这是结果:

enter image description here

正如你可以从代码中看到我试图取消对特定的ImageView待定毕加索的请求,但这还不够。这个问题能解决吗?

+1

我想你可以把onScroll听者在Rec​​yclerView,只有当它停止滚动启动图像加载。 – X3Btel

+0

@ X3Btel不错的想法,我会尝试 – fillobotto

回答

4

我通过在ViewHolder添加字段包含AsyncTask相对于该项目解决了。在bindView函数中,我设置了AsyncTask.cancel(true),并且在使用Picasso.with(...).load(...)应用检索的图像之前,我在检查isCancelled()的任务内执行了此操作。这本身解决了闪烁。

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

该解决方案让我觉得这个问题是时间从MediaStore图像检索和时间之间的AsyncTask内intercurring量当图像被实际应用到ImageView毕加索。

+0

只是'onViewRecycled'就足以解决问题不需要改变异步任务 –

0

评论这些线

闪烁正在发生,因为绑定不仅是一次单项所以它一再呼吁单列打电话,要设置为null的每次还取决于其设置视图。产生闪烁。

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

不幸的是不是解决方案。由于同一ImageView上的多个请求而发生闪烁,而不是因为我设置为空。 – fillobotto

+0

闪烁正在发生becasue绑定不会为单个项目调用一次,因此它会一次又一次调用单个行,并且您每次都设置为空,并且还会在其上设置视图。产生闪烁。 –

+0

感觉的第一部分是正确的,第二部分不是。顺便说一句,我尝试了你的建议,结果是完全一样的。 – fillobotto

0

您使用的异步任务是完全错误的。

首先,你永远不应该执行一个匿名的异步任务。

其次,在适配器中摆脱这种异步任务,因为它似乎是导致问题。

获取适配器的数据,然后显示它。

顺便说一句,终止毕加索不如做到这样:

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

好,我使用的AsyncTask来检索图像MediaStore。这个过程似乎需要一些时间,并使滚动非常缓慢 – fillobotto