Android的实时Blur渲染---BlurDrawable
2016-09-23 / modified at 2022-04-04 / 1.2k words / 4 mins
️This article has been over 2 years since the last update.

目前有很多厂家/APP都在做Blur,比如魅族高端机(mx4以后的机器)提供了实时模糊选项,并给第三方相应的sdk。有的第三方app(比如最美壁纸,雅虎天气,开眼等)看似使用了实时模糊,实际上只是两张截图的alpha变换而已,网上大多数开源项目亦是如此;还有的第三方库,比如14年非常火的GlassActionbar,的确可以实现动态模糊,可是fps不太满意,而且内部使用了AsyncTask作为异步处理,考虑到线程池中创建,销毁,上下文切换的损失,也不太敢用。

BlurDrawable

最后,面向Github编程的我,经过各种坑,终于抄到了一个项目
500px-android-blur,这个项目貌似是一位肉翻的国人写的,代码质量很高,英文写的也很正统。经过阅读参考代码后,我把自定义view中的相关处理port到了Drawable中,这个就是今天的主题BlurDrawable,本文就此分享一下使用与源码。

使用

首先要有两个view,一个盖在另一个上面,比如说最常见的NavigationBarRecyclerView,它们放在一个FrameLayout中,然后进行如下代码:

1
2
3
4
5
6
7
8
9
10
11
BlurDrawable drawable = new BlurDrawable(mRvFragCard);
//模糊半径,越大图片越平均
drawable.setBlurRadius(12);
//图片抽样率,这里把图片缩放小了8倍
drawable.setDownsampleFactor(8);
//模糊后再覆盖的一层颜色
drawable.setOverlayColor(Color.argb(128, 0xff, 0xff, 0xff));
//顶部View与底部View的相对坐标差,由于这里都是(0,0)起步
//所以相对位置偏移为0
drawable.setDrawOffset(0,0);
mNavigationBar.setBackgroundDrawable(drawable);

如果你的需求只是静态模糊,那么所有东西就已经写完了,如果需要在滚动时进行实时计算,按照下面写的在滚动时通知重绘即可。

1
2
3
4
5
6
7
8
9
10
mRvFragCard.addOnScrollListener(new RecyclerView.OnScrollListener() {

@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//可能需要这个,当然这里由于位置比较巧,就没有用
//drawable.setDrawOffset(0,0);
//通知重绘
ViewCompat.postInvalidateOnAnimation(mNavigationBar);
}
});

这里还有个小技巧,如果按照默认布局的话,启动时RecyclerView的第一个item会嵌入在NavigationBar中无法点击,所以需要预留一些padding空间

1
2
3
4
<android.support.v7.widget.RecyclerView
......
android:paddingTop="48dp"
android:clipToPadding="false"/>

这样使用效果就如上面gif一样了,gif压缩了部分细节,建议下载完整示例程序查看

最后,当不需要时,释放资源

1
2
3
4
5
6
@Override protected void onDestroy() {
super.onDestroy();
if (drawable != null) {
drawable.onDestroy();
}
}

兼容性与效率

基于自带的RenderScript进行渲染,厂商与谷歌都进行了专业的优化,所以兼容性与效率当然是棒棒哒,然而在国内…

1. 兼容性正常的机器:
  • API17及以上: 将自动开启Blur,经过玄学曲线进行测试,构造耗时4ms,滚动时fps在8ms左右(在高通615/1080P/2G下进行测试的,615的尿性大家都知道,也就是说目前千元机均毫无压力);
  • API17以下: 它将自动根据OverlayColor当做ColorDrawable进行纯色绘制(iOS关闭blur后也是这样的效果)。如果非要强迫用blur的话,可以使用RenderScript的兼容包,但不推荐。
2. 兼容性不正常的机器:

根据群里大神的建议,某些国产设备由于ROM偷工减料,可能会出现RenderScript崩溃或者性能不达标的坑。不过跑了下Testin云测,随机100个主流设备并没有出现因为RenderScript崩溃的案例,但是出现了一个山寨机花屏的问题。另外根据友盟指数可以看到,TOP50的设备中,除了酷派的老设备,其它都是比较放心的不会偷工减料的厂商。

综上,我个人还是建议集成,第一,它只是个单文件,定制强,体积小的不像实力派;第二,可以通过drawable.setEnabled(false)手动控制默认开关;

开发者如果想在兼容性与美观性保持平衡的话,建议在设置中默认关闭,并引导用户开启;或者将API17手动提高到19或者20,以获得更好的兼容性。

原理分析

实时渲染非常类似于DSP,底层是c语言实现的高斯模糊。首先根据DownsampleFactor进行绘制与抽样,获取到被覆盖view的rawBitmap,这样bitmap的信息量减小了几个数量级(比如1920x1080缩小到136x244),接着通过Renderscript进行卷积运算编码后输出,最后将处理好的bitmap用canvas拉伸绘制到表层的view中。注释已经很详细了,这里就不说了。

工具下载

BlurDrawable

例子下载

  1. 本文的例子
  2. 评论中要的使用Toolbar的简单例子