最近要做类似网易云音乐背景高斯模糊的效果, 同时也想让背景变化时不要那么生硬, 就是下面这个效果
Google一番后决定用TransitionDrawable, 由于是配合UniversalImageLoader使用, 所以只需要实现一个BitmapDisplayer作为UIL的配置项就行了.
最初的代码是这样写的
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
就可以了.
简单明了. 实际使用中, 发现app占用的内存越来越高, 但只要退出Activity
, 一两次GC之后内存就会降下来, 基本可以确定是这段代码造成了内存泄露.
问题出在这句代码
Drawable oldDrawable = imageview.getDrawable();
乍一看这句代码逻辑是没有问题的, 每次我们都是将旧的Drawable
作为第一层, 新的Drawable
作为第二层创建TransitionDrawable
, 但是注意我们是创建的TransitionDrawable
, 并将它设给ImageView
, 也就是说我们调用getDrawable
拿到的也是TransitionDrawable
, 一个TransitionDrawable
其实是持有多个Drawable
的, 在这里是持有两个.
程序进行第一次渐变动画后, ImageView
中的TransitionDrawable
持有两个Drawable
, 第二次渐变动画, 我们将TransitionDrawable
和新的BitmapDrawable
组合在一起创建一个新的TransitionDrawable
.
简单示意一下ImageView
持有的Drawable
:
第一次渐变后:
TransitionDrawable(drawable0, drawable1)
第二次渐变后:
TransitionDrawable(
TransitionDrawable(drawable0, drawable1),
drawable2
)
第三次渐变后:
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);
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。