2016-07-28 127 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