最近要作相似网易云音乐背景高斯模糊的效果, 同时也想让背景变化时不要那么生硬, 就是下面这个效果app
Google一番后决定用TransitionDrawable, 因为是配合UniversalImageLoader使用, 因此只须要实现一个BitmapDisplayer做为UIL的配置项就好了.ide
最初的代码是这样写的动画
private static class DrawableFadeDisplayer implements BitmapDisplayer { private final int durationMillis; public DrawableFadeDisplayer(int durationMillis) { this.durationMillis = durationMillis; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { ImageView imageview = (ImageView) imageAware.getWrappedView(); Drawable oldDrawable = imageview.getDrawable(); TransitionDrawable td = new TransitionDrawable(new Drawable[] { oldDrawable==null?(new ColorDrawable(Color.TRANSPARENT)):oldDrawable, new BitmapDrawable(Resources.getSystem(), bitmap) }); imageview.setImageDrawable(td); td.startTransition(durationMillis); } }
最关键的部分是display
中的代码, 首先获取了旧的Drawable
, 而后和新生成的BitmapDrawable
一块儿构造一个TransitionDrawable
, 最后调用startTransition
就能够了.this
简单明了. 实际使用中, 发现app占用的内存愈来愈高, 但只要退出Activity
, 一两次GC以后内存就会降下来, 基本能够肯定是这段代码形成了内存泄露.code
问题出在这句代码内存
Drawable oldDrawable = imageview.getDrawable();
乍一看这句代码逻辑是没有问题的, 每次咱们都是将旧的Drawable
做为第一层, 新的Drawable
做为第二层建立TransitionDrawable
, 可是注意咱们是建立的TransitionDrawable
, 并将它设给ImageView
, 也就是说咱们调用getDrawable
拿到的也是TransitionDrawable
, 一个TransitionDrawable
实际上是持有多个Drawable
的, 在这里是持有两个.get
程序进行第一次渐变更画后, ImageView
中的TransitionDrawable
持有两个Drawable
, 第二次渐变更画, 咱们将TransitionDrawable
和新的BitmapDrawable
组合在一块儿建立一个新的TransitionDrawable
.
简单示意一下ImageView
持有的Drawable
:
第一次渐变后:it
TransitionDrawable(drawable0, drawable1)
第二次渐变后:io
TransitionDrawable( TransitionDrawable(drawable0, drawable1), drawable2 )
第三次渐变后:class
TransitionDrawable( TransitionDrawable( TransitionDrawable(drawable0, drawable1), drawable2 ), drawable3 )
这样ImageView
致使不能被回收的Drawable
数量愈来愈多, 最终OOM.
因此咱们正确的作法不该该是直接将getDrawable
的值拿来当第一层Drawable
, 而是先判断一下这个值的类型, 若是是TransitionDrawable
, 应该获取它第二层Drawable
做为咱们的第一层, 这样原来的第一层Drawable
就会失去到GC Roots的引用链, 从而能够被回收.
固然另外一种思路是TransitionDrawable
动画完成以后再将新的BitmapDrawable
设给ImageView
, 但并无这个监听器, 最简单便捷的仍是上面的思路.
最终代码修改为下面的样子, 主要是须要判断getDrawable
的类型, 若是是TransitionDrawable
, 就获取第二层Drawable
.
private static class DrawableFadeDisplayer implements BitmapDisplayer { private final int durationMillis; public DrawableFadeDisplayer(int durationMillis) { this.durationMillis = durationMillis; } @Override public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) { ImageView imageview = (ImageView) imageAware.getWrappedView(); Drawable oldDrawable = imageview.getDrawable(); Drawable oldBitmapDrawable = null; if (oldDrawable == null) { oldBitmapDrawable = new ColorDrawable(Color.TRANSPARENT); } else if (oldDrawable instanceof TransitionDrawable) { oldBitmapDrawable = ((TransitionDrawable) oldDrawable).getDrawable(1); } else { oldBitmapDrawable = oldDrawable; } TransitionDrawable td = new TransitionDrawable(new Drawable[] { oldBitmapDrawable, new BitmapDrawable(Resources.getSystem(), bitmap) }); imageview.setImageDrawable(td); td.startTransition(durationMillis); } }