android - Listview items with Animation do not render properly -
have gridview populated bitmaps animate in when bitmap loads asynchronously. when flinging gridview of items not render properly. animation trigger bitmap not show up.
i have confirmed bitmap indeed there (at least data) doesn't render.
this happens when flinging gridview happens on slow scroll well. seems view recycling not working properly. here code:
listview adapter:
@override public view getview(int position, view convertview, viewgroup parent) { if(convertview == null){ convertview = new flipanimatedcacheableimage(mcontext); } final imageinfo info = mapitem(getitem(position)); flipanimatedcacheableimage image = (flipanimatedcacheableimage)convertview; image.resetstate(); image.settitle(info.title); image.setsubtitle(info.subtitle); image.loadimage(info.imgurl, false); return convertview; }
code flipanimatedcacheableimage:
public class flipanimatedcacheableimage extends framelayout { private static final string tag = flipanimatedcacheableimage.class.getcanonicalname(); protected static final long duration = 300; private imageview mplaceholder; private networkedcacheableimageview mcacheableimage; private view mtextcontainer; private textview mtitletv; private textview msubtitletv; private view mprogress; private imageloadlistener mlistener = new imageloadlistener() { private boolean isshown; @override public void onimageloaded(boolean animate) { if(animate){ mprogress.setvisibility(view.gone); mplaceholder.setvisibility(view.visible); mplaceholder.setrotationy(0); mcacheableimage.setvisibility(view.visible); mcacheableimage.setrotationy(-90); mplaceholder.animate().rotationy(90).setduration(duration).start(); mcacheableimage.animate().rotationy(0).setduration(duration).setstartdelay(duration).start(); if(!textutils.isempty(mtitletv.gettext()) || !textutils.isempty(msubtitletv.gettext())){ mtextcontainer.setvisibility(view.visible); mtextcontainer.setalpha(0); mtextcontainer.animate().alpha(1).setduration(duration).setstartdelay(duration * 2).start(); } else{ mtextcontainer.setvisibility(view.gone); } isshown = true; flipanimatedcacheableimage.this.invalidate(); } else{ mplaceholder.setvisibility(view.gone); mprogress.setvisibility(view.gone); mcacheableimage.setvisibility(view.visible); mcacheableimage.setrotationy(0); mcacheableimage.clearanimation(); if(!textutils.isempty(mtitletv.gettext()) || !textutils.isempty(msubtitletv.gettext())){ mtextcontainer.setvisibility(view.visible); mtextcontainer.setalpha(1); } else{ mtextcontainer.setvisibility(view.gone); } flipanimatedcacheableimage.this.invalidate(); } } }; public flipanimatedcacheableimage(context context, boolean islarge) { this(context, null, 0, islarge); } public flipanimatedcacheableimage(context context) { this(context, null); } public flipanimatedcacheableimage(context context, attributeset attrs) { this(context, attrs, 0, false); } public flipanimatedcacheableimage(context context, attributeset attrs, int defstyle, boolean islarge) { super(context, attrs, defstyle); layoutinflater inflater = layoutinflater.from(context); inflater.inflate(r.layout.item_image_thumbnail, this); mcacheableimage = (networkedcacheableimageview)this.findviewbyid(r.id.image_view); mplaceholder = (imageview)this.findviewbyid(r.id.place_holder); mprogress = this.findviewbyid(r.id.progressbar); // used small images mtextcontainer = this.findviewbyid(r.id.text_container); mtitletv = (textview)this.findviewbyid(r.id.text_title); msubtitletv = (textview)this.findviewbyid(r.id.text_sub_title); // listener animate after loading mcacheableimage.setloadlistener(mlistener); // set default state mtextcontainer.setvisibility(view.gone); mtitletv.setvisibility(gone); msubtitletv.setvisibility(gone); if(islarge){ // adjust size correct dimensions, ignore titleand subtitle framelayout.layoutparams imageparams = new framelayout.layoutparams((int)context.getresources() .getdimension(r.dimen.grid_image_width_large), (int)context.getresources().getdimension( r.dimen.grid_image_height_large)); findviewbyid(r.id.content_wrapper).setlayoutparams(imageparams); } // this.setonclicklistener(listener); resetstate(); } public void resetstate() { mcacheableimage.setvisibility(view.visible); mcacheableimage.setrotationy(0); mprogress.setvisibility(view.visible); mplaceholder.setvisibility(view.visible); mtextcontainer.setvisibility(view.gone); mtitletv.setvisibility(gone); msubtitletv.setvisibility(gone); } public boolean loadimage(string url, boolean fullsize) { return mcacheableimage.loadimage(url, fullsize); } public void settitle(string title) { mtitletv.settext(title); if(!textutils.isempty(title)){ mtitletv.setvisibility(view.visible); } else{ mtitletv.setvisibility(view.gone); } } public void setsubtitle(string subtitle) { msubtitletv.settext(subtitle); if(!textutils.isempty(subtitle)){ msubtitletv.setvisibility(view.visible); } else{ msubtitletv.setvisibility(view.gone); } } }
flipanimatedcache request image cache written chris banes here https://github.com/chrisbanes/android-bitmapcache
here code:
/******************************************************************************* * copyright 2011, 2013 chris banes. * * licensed under apache license, version 2.0 (the "license"); * may not use file except in compliance license. * may obtain copy of license @ * * http://www.apache.org/licenses/license-2.0 * * unless required applicable law or agreed in writing, software * distributed under license distributed on "as is" basis, * without warranties or conditions of kind, either express or implied. * see license specific language governing permissions , * limitations under license. *******************************************************************************/ /** * simple extension of cacheableimageview allows downloading of images of * internet. * * code isn't production quality, works enough sample.s * * @author chris banes * */ public class networkedcacheableimageview extends cacheableimageview { private static final string tag = networkedcacheableimageview.class.getcanonicalname(); public interface imageloadlistener { public void onimageloaded(boolean animate); } /** * task fetches bitmap specified url , wraps in * wrapper. implementation not 'best practice' or production ready * code. */ private static class imageurlasynctask extends asynctask<string, void, cacheablebitmapdrawable> { private final bitmaplrucache mcache; private final weakreference<imageview> mimageviewref; private final weakreference<networkedcacheableimageview> viewref; private final bitmapfactory.options mdecodeopts; private final imageloadlistener mloadlistener; private boolean outofmemoryfailure; private string murl; imageurlasynctask(imageview imageview, bitmaplrucache cache, bitmapfactory.options decodeopts, imageloadlistener listener, networkedcacheableimageview view) { mcache = cache; mloadlistener = listener; mimageviewref = new weakreference<imageview>(imageview); viewref = new weakreference<networkedcacheableimageview>(view); mdecodeopts = decodeopts; } @override protected cacheablebitmapdrawable doinbackground(string... params) { try{ // return if imageview has disappeared. if(null == mimageviewref.get()){ return null; } murl = params[0]; // we're not on main thread can check caches cacheablebitmapdrawable result = mcache.get(murl, mdecodeopts); if(null == result){ log.w("cache", "downloading: " + murl); // bitmap isn't cached download web httpurlconnection conn = (httpurlconnection)new url(murl).openconnection(); inputstream = new bufferedinputstream(conn.getinputstream()); // add cache result = mcache.put(murl, is, mdecodeopts); } else{ log.w("cache", "got disk cache: " + murl); } return result; } catch(ioexception e){ log.e("error downloading image", e.tostring()); } catch(outofmemoryerror e){ log.e(tag, "running out of memory, trimming image memory cache"); outofmemoryfailure = true; } return null; } @override protected void onpostexecute(final cacheablebitmapdrawable result) { // super.onpostexecute(result); if(outofmemoryfailure || result == null || !result.hasvalidbitmap()){ mcache.trimmemory(); // viewref.get().loadimageasync(murl, mdecodeopts); log.e(tag, "image did not load::" + murl); } else{ if(buildconfig.debug){ log.d("bitmapcache", "networkedcacheableimageview.imageurlasynctask.onpostexecute() width:" + result.getbitmap().getwidth()); log.d("bitmapcache", "networkedcacheableimageview.imageurlasynctask.onpostexecute() height:" + result.getbitmap().getheight()); } log.i(tag, "result :" + murl + " mimageviewref::" + mimageviewref + " outofmemoryfailure::" + outofmemoryfailure); if(result != null){ log.i(tag, "result object :" + murl + " result:hasvalidbitmap" + result.hasvalidbitmap() + " result:isreferencedbycache" + result.isreferencedbycache() + " result.isbeingdisplayed() " + result.isbeingdisplayed()); } runnable runnable = new runnable() { @override public void run() { if(mimageviewref != null){ final imageview iv = mimageviewref.get(); log.e(tag, "result image view reference :" + murl + " view ref::" + iv); if(null != iv){ iv.setimagedrawable(result); if(mloadlistener != null){ mloadlistener.onimageloaded(true); } } } } }; handler handler = new handler(); handler.postdelayed(runnable, 50); } super.onpostexecute(result); } } private final bitmaplrucache mcache; private imageurlasynctask mcurrenttask; private imageloadlistener mlistener; public networkedcacheableimageview(context context, attributeset attrs) { super(context, attrs); mcache = watchapplication.getbitmapcache(); } public void setloadlistener(imageloadlistener listener) { mlistener = listener; } public void removelistener() { mlistener = null; } /** * loads bitmap. * * @param url * - url of image * @param fullsize * - whether image should kept @ original size * @return true if bitmap found in cache */ public boolean loadimage(string url, final boolean fullsize) { setimagedrawable(null); // first check whether there's task running, if cancel if(textutils.isempty(url)) return false; if(null != mcurrenttask){ mcurrenttask.cancel(false); } // check see if memory cache has bitmap. can // safely // on main thread. bitmapdrawable wrapper = mcache.getfrommemorycache(url); if(null != wrapper){ // cache has it, display if(buildconfig.debug){ log.w(tag, "cache. found in memory:" + url); } setimagedrawable(wrapper); if(mlistener != null){ mlistener.onimageloaded(false); } return true; } else{ // memory cache doesn't have url, threaded request... bitmapfactory.options decodeopts = null; if(!fullsize){ decodeopts = new bitmapfactory.options(); // decodeopts.indensity = displaymetrics.density_xhigh; decodeopts.inpurgeable = true; decodeopts.outheight = this.getheight(); decodeopts.outwidth = this.getwidth(); } loadimageasync(url, decodeopts); return false; } } public void loadimageasync(string url, bitmapfactory.options decodeopts) { mcurrenttask = new imageurlasynctask(this, mcache, decodeopts, mlistener, this); if(build.version.sdk_int >= build.version_codes.honeycomb){ sdk11.executeonthreadpool(mcurrenttask, url); } else{ mcurrenttask.execute(url); } } }
thanks in advance help!
two things worth trying -
for chaining animation operations use listener:
mplaceholder.animate() .alpha(0f) .scalex(0.9f) .scaley(0.9f) .rotationy(90) .setduration(duration) .setlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { mcacheableimage.setimagedrawable(drawable); mcacheableimage.animate() .alpha(1f) .scaley(1f) .scalex(1f) .rotationy(0) .setduration(duration) .setlistener(null); } });
use sethastransientstate() ensure views not recycled in listview/gridview. see devbyte video more info.
Comments
Post a Comment