recycler 刷新图片闪烁,在RecyclerView中滚动时图像闪烁

I'm trying to display a list of songs found on the device requesting data directly from the MediaStore. I'm using a RecyclerView and an adapter that uses a CursorAdapter to get data from MediaStore.

When adapter's onBindViewHolder is called, the request is passed to the bindView function of the CursorAdapter the all visual elements are set.

public class ListRecyclerAdapter3 extends RecyclerView.Adapter {

// 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 {

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

}

}

The problematic part is image loading with is made of two parts:

With the albumId I got from the Cursor, I need use ContentResolver to get album art file path

Load the image into the ImageView using the file path

These two passages need to be done in background otherwise the scrolling will become very laggy. In the bindView function I called a AsyncTask the does the job, but the problem is that, while scrolling fast, several image requests are elaborated and this is the result:

jFTum.gif

As you can see from the code I tried to cancel pending Picasso's requests on a specific ImageView, but that's not enough. Can this problem be fixed?

解决方案

I solved by adding a field in the ViewHolder containing the AsyncTask relative to that item. In the bindView function I fisrt set the AsyncTask.cancel(true) and, inside the task I made a check isCancelled() before applying the retrieved image using Picasso.with(...).load(...). This itself solved flickering.

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 {

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

}

}

For the sake of completeness, this also remove image from recycled items and set a placeholder.

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

}

This solution made me think that the problem was the amount of time intercurring inside the AsyncTask between image retrieval from MediaStore and the time when the image is actually applied into the ImageView from Picasso.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,你需要从数据库查询出文本和图片的数据,然后将其封装到一个数据模型,例如: ```java public class DataModel { private String text; private byte[] image; public DataModel(String text, byte[] image) { this.text = text; this.image = image; } public String getText() { return text; } public byte[] getImage() { return image; } } ``` 接着,你需要创建一个 `RecyclerView`,并为其设置一个布局管理器和一个适配器。在适配器,你需要绑定数据到 `ViewHolder` ,并将文本和图片显示在 `TextView` 和 `ImageView` 。例如: ```java public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private List<DataModel> dataList; public MyAdapter(List<DataModel> dataList) { this.dataList = dataList; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder, int position) { DataModel data = dataList.get(position); holder.textView.setText(data.getText()); Bitmap bitmap = BitmapFactory.decodeByteArray(data.getImage(), 0, data.getImage().length); holder.imageView.setImageBitmap(bitmap); } @Override public int getItemCount() { return dataList.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView textView; public ImageView imageView; public ViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.text_view); imageView = itemView.findViewById(R.id.image_view); } } } ``` 最后,在你的活动或片段,你需要创建一个数据库帮助器类来查询数据库的数据,并将其传递给适配器。例如: ```java public class MyActivity extends AppCompatActivity { private RecyclerView recyclerView; private MyAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); adapter = new MyAdapter(getData()); recyclerView.setAdapter(adapter); } private List<DataModel> getData() { List<DataModel> dataList = new ArrayList<>(); // 查询数据库的数据并添加到 dataList return dataList; } } ``` 需要注意的是,由于图片数据较大,建议将其存储在本地文件系统或云存储服务,并在数据库存储图片的 URL 或文件路径。在适配器,通过 URL 或文件路径加载图片并显示在 `ImageView` 即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值