android - Image flickering while scrolling in RecyclerView -
i'm trying display list of songs found on device requesting data directly mediastore. i'm using recyclerview
, adapter uses cursoradapter
data mediastore. when adapter's onbindviewholder
called, request passed bindview
function of cursoradapter
visual elements set.
public class listrecycleradapter3 extends recyclerview.adapter<listrecycleradapter3.songviewholder> { // patch: because recyclerview.adapter in current form doesn't natively support // cursors, "wrap" cursoradapter teh job // 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 binding operation cursor loader mcursoradapter.getcursor().movetoposition(position); mcursoradapter.bindview(holder.itemview, mcontext, mcursoradapter.getcursor()); } @override public songviewholder oncreateviewholder(viewgroup parent, int viewtype) { // passing inflater job cursor-adapter view v = mcursoradapter.newview(mcontext, mcursoradapter.getcursor(), parent); return new songviewholder(v); } }
the problematic part image loading made of 2 parts:
- with albumid got
cursor
, need usecontentresolver
album art file path - load image
imageview
using file path
these 2 passages need done in background otherwise scrolling become laggy. in bindview
function called asynctask
job, problem that, while scrolling fast, several image requests elaborated , result:
as can see code tried cancel pending picasso's requests on specific imageview
, that's not enough. can problem fixed?
i solved adding field in viewholder
containing asynctask
relative item. in bindview
function fisrt set asynctask.cancel(true)
and, inside task made check iscancelled()
before applying retrieved image using picasso.with(...).load(...)
. 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<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); } }
for sake of completeness, remove image recycled items , set 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 problem amount of time intercurring inside asynctask
between image retrieval mediastore , time when image applied imageview
picasso.
Comments
Post a Comment