Android - ListView images shuffled when scrolling -
i'm trying make listview dynamically loaded images, using asynctask download image , set listview. problem that, while scrolling down, images randomly changed.
public class getallcustomerlistviewadapter extends baseadapter { private jsonarray dataarray; private activity activity; private static layoutinflater inflater = null; private static final string baseurlforimage = "http://192.168.254.1/contact/images/"; public getallcustomerlistviewadapter(jsonarray jsonarray, activity a) { this.dataarray = jsonarray; this.activity = a; inflater = (layoutinflater) this.activity.getsystemservice(context.layout_inflater_service); } @override public int getcount() { return this.dataarray.length(); } @override public object getitem(int position) { return position; } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { // set convert view if null final listcell cell; if (convertview == null) { convertview = inflater.inflate(r.layout.get_all_customer_list_view_cell, null); cell = new listcell(); cell.fullname = (textview) convertview.findviewbyid(r.id.customer_full_name); cell.age = (textview) convertview.findviewbyid(r.id.customer_age); cell.mobile = (imageview) convertview.findviewbyid(r.id.customer_mobile); convertview.settag(cell); } else { cell = (listcell) convertview.gettag(); } // change data of cell try { jsonobject jsonobject = this.dataarray.getjsonobject(position); cell.fullname.settext(jsonobject.getstring("firstname")+" "+jsonobject.getstring("lastname")); cell.age.settext(" "+jsonobject.getint("age")); string nameofimage = jsonobject.getstring("id"); string urlforimageinserver = "http://192.168.254.1/contact/getimage.php?id="+nameofimage; new asynctask<string, void, bitmap>() { @override protected bitmap doinbackground(string... params) { string url = params[0]; bitmap icon = null; try { inputstream in = new java.net.url(url).openstream(); icon = bitmapfactory.decodestream(in); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } return icon; } protected void onpostexecute(bitmap result) { cell.mobile.setimagebitmap(result); } }.execute(urlforimageinserver); } catch (jsonexception e) { e.printstacktrace(); } return convertview; } private class listcell { private textview fullname; private textview age; private imageview mobile; }
}
i did (at 1am actually)- problem listview actively recycling views. therefore, gets old views still holds bitmaps.
the following need:
[note: check if bitmap cached earlier. if not, steps below]
- cache bitmap first before trying load it
- cache targeted imageview (and keep track of bitmap supposed go bitmap). also, empty out imageview in case still contains bitmap previous recycled item (
imageview.setimagebitmap(null);
) - double check cached imageview has not been cached previous item already. if cached previously, somehow state bitmap being received should not loaded (i did storing imageview every spot, , once bitmap received, if row's imageview null again don't load in bitmap)
- load bitmap (as mentioned above) if still should- ie, if imageview wasn't recycled again. mentioned earlier
the solution albeit bit complicated works , smoothly if implemented correctly. if you'd example, feel free check 1 out in action here in current project.
edit requested example:
here not-so-pseudo pseudocode. main part still need implement lazy loading system (which whole other topic). once again, feel free visit project linked above , @ "imageloadernocache" networkutil.
public class getallcustomerlistviewadapter extends baseadapter { private jsonarray dataarray; private activity activity; private static layoutinflater inflater = null; private static final string baseurlforimage = "http://192.168.254.1/contact/images/"; // list caches bitmaps target imageviews, in order of rows private arraylist<rowviewdata> rowviewdatalist; public getallcustomerlistviewadapter(jsonarray jsonarray, activity a) { this.dataarray = jsonarray; this.activity = a; inflater = (layoutinflater) this.activity.getsystemservice(context.layout_inflater_service); // initialize list rowviewdatalist = new arraylist<rowviewdata>(dataarray.length()); // more of preference, add in every item rowviewdatalist for(int = 0; < dataarray.length(); ++i) { // create new, empty rowviewdata instance , put list rowviewdata rowdata = new rowviewdata(); rowviewdatalist.add(rowdata); } } @override public int getcount() { return this.dataarray.length(); } @override public object getitem(int position) { return position; } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { // set convert view if null final listcell cell; if (convertview == null) { convertview = inflater.inflate(r.layout.get_all_customer_list_view_cell, null); cell = new listcell(); cell.fullname = (textview) convertview.findviewbyid(r.id.customer_full_name); cell.age = (textview) convertview.findviewbyid(r.id.customer_age); cell.mobile = (imageview) convertview.findviewbyid(r.id.customer_mobile); convertview.settag(cell); } else { cell = (listcell) convertview.gettag(); } // change data of cell try { jsonobject jsonobject = this.dataarray.getjsonobject(position); cell.fullname.settext(jsonobject.getstring("firstname")+" "+jsonobject.getstring("lastname")); cell.age.settext(" "+jsonobject.getint("age")); // rowviewdata position rowviewdata currowviewdata = rowviewdatalist.get(position); // if bitmap cached, use if(currowviewdata.bitmap != null) { cell.mobile.setimagebitmap(currowviewdata.bitmap); } // otherwise, gotta real work else { // first, set cell's imageview's bitmap null // [in case recycled view bitmap] cell.mobile.setimagebitmap(null); // then, start caching system string nameofimage = jsonobject.getstring("id"); string urlforimageinserver = "http://192.168.254.1/contact/getimage.php?id="+nameofimage; // create system load images lazily , receive bitmaps // @ imageloadernocache example. feel free reuse /** * = adapter should implement interface. below corresponding method * url = image source url * position = position bitmap belongs to. returned along bitmap reference */ lazyimageloader.getbitmap(this, url, position); // cache/target imageview later currowviewdata.targetimageview = cell.mobile; // double check imageview not being targeted elsewhere // ie, 1 of recycling fixes [else, 2 bitmaps load @ once, etc] // also, more efficient for(int = 0; < rowviewdatalist.size(); ++i) { // if current position, skip logic round if(i == position) continue; // rowviewdata round // yeah... should've used "cell" instead of row you... rowviewdata checkrowdata = rowviewdatalist.get(i); // if targeted imageview same, null-ify if(checkrowdata.targetimageview == currowviewdata.targetimageview) { // old 1 should null bitmap should not load // current recycled item checkrowdata.targetimageview = null; // personally, knew there 1 copy @ time // tried save time breaking out here. feel free // cautious , comment out break statement break; } } } } catch (jsonexception e) { e.printstacktrace(); } return convertview; } /** * should called part of interface lazyimageloader * @param bitmap - lazily loaded bitmap requested earlier * @param position - row/cell position bitmap requested */ public void getbitmapforposition(bitmap bitmap, int position) { // rowviewdata instance row/position rowviewdata currowdata = rowviewdatalist.get(position); // cache bitmap currowdata.bitmap = bitmap; // check if bitmap should loaded still if(currowdata.targetimageview != null) { currowdata.targetimageview.setimagebitmap(bitmap); // not sure if strictly necessary, practice make cached view null currowdata.targetimageview = null; } } private class listcell { private textview fullname; private textview age; private imageview mobile; } // holds data lazily loading image bitmap private class rowviewdata { public imageview targetimageview; public bitmap bitmap; } }
Comments
Post a Comment