在 Android 下进行实时模糊渲染

本文讲的是在 Android 下进行实时模糊渲染,


模糊渲染

模糊渲染能生动地表达内容间的层次感。当专注于当前特定内容的时候,它允许用户维持相对的上下文,即使模糊层下面的内容发生了视差移动或者动态变化。

在IOS开发中,我们首先可以通过构造UIVisualEffectView获得这种模糊效果:

UIVisualEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
UIVisualEffectView *visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];

接着我们可以添加visualEffectView到视图层中,那么在它之下的内容都会动态渲染模糊效果。

在Android中的现状

虽然在Android中并没有直接的方法实现模糊渲染,但我们依然能见到些十分优秀的例子比如Yahoo Weather应用,见Nicholas Pomepuy的博文,然而,它是通过缓存一张预先渲染模糊的背景图片实现的。

虽然这种方法挺有效果,但并不是我们想要的。在500px社区,图片并不是用作背景而是焦点内容,这意味着图片可以随意改变甚至迅速改变,即使它们被覆盖在模糊层之下。我们的Android应用就是个十分典型的例子。比如,当用户滑到下一页时,图片会向反方向移动并淡出,通过适当地维护多个预先渲染模糊的图片是很难满足这种需求的。

通过自定义View的OnDraw方法

我们的需求是希望能实现一个模糊视图,它能实时动态地模糊渲染在它之下的视图。我们最终想要的代码最好能尽量简单例如直接让模糊视图拥有一份被模糊视图的引用:

    blurringView.setBlurredView(blurredView);

然后当被模糊视图改变时 - 不管是内容的改变(如显示张新的图片)、视图的移动、或者是视图动画,我们都需要刷新模糊视图:

    blurringView.invalidate();

为了实现模糊视图,我们需要继承View类然后重写onDraw()方法来渲染模糊效果:

    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    // 让被模糊视图的draw()方法在私有的画布上绘制
    mBlurredView.draw(mBlurringCanvas);

    // 模糊私有画布的位图并传递给mBlurredBitmap
    blur();

    // 经过转换后将mBlurredBitmap绘制在模糊视图的默认画布上
    canvas.save();
    canvas.translate(mBlurredView.getX() - getX(), mBlurredView.getY() - getY());
    canvas.scale(DOWNSAMPLE_FACTOR, DOWNSAMPLE_FACTOR);
    canvas.drawBitmap(mBlurredBitmap, 0, 0, null);
    canvas.restore();
    }

这里的关键是当模糊视图重绘的时候,我们会通过对被模糊视图的引用来调用它的draw方法,同时它会在我们私有的画布上绘画(译者注:对该画布的操作最终会作用到我们私有的位图上):

    mBlurredView.draw(mBlurringCanvas);

(通过这种途径访问其它的视图的draw方法十分有参考价值,我们也可以实现一个放大镜或者用来标注的视图,相对于模糊渲染,放大镜或者标注的区域更需要放大。)

下面的想法在Nicholas Pomepuy的博文中有谈到,我们结合二次抽样与RenderScript进行快速处理。在我们完成模糊视图的私有画布mBlurringCanvas的初始化后二次抽样也设置完成:

    int scaledWidth = mBlurredView.getWidth() / DOWNSAMPLE_FACTOR;
    int scaledHeight = mBlurredView.getHeight() / DOWNSAMPLE_FACTOR;

    mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ARGB_8888);
    mBlurringCanvas = new Canvas(mBitmapToBlur);

通过了上面的设置后再适当地初始化RenderScript。那么上文onDraw()调用的blur()方法就简单多了:

    mBlurInput.copyFrom(mBitmapToBlur);
    mBlurScript.setInput(mBlurInput);
    mBlurScript.forEach(mBlurOutput);
    mBlurOutput.copyTo(mBlurredBitmap);

注意此时mBlurredBitmap已经渲染好了,余下的工作是onDraw()方法对它适当的移动和缩放后绘制到模糊视图默认画布中。

实现细节

对于完全的实现,我们需要留心多个技术细节。首先,我们意识到,8个单位的缩放采样以及15个单位的模糊半径就能很好地呈现我们想要的效果。当然,或许对你来说,别的参数才能满足你的需求。

其次,在模糊位图的边缘处我们遇到了一些RenderScript的历史遗留问题,为了应对这个问题,我们对宽度和高度缩放到近似4倍。

    // The rounding-off here is for suppressing RenderScript artifacts at the edge.
    scaledWidth = scaledWidth - (scaledWidth % 4) + 4;
    scaledHeight = scaledHeight - (scaledHeight % 4) + 4;

第三,我们为了更好地保证性能,需要创建两张位图分别是mBitmapToBlur做为私有画布mBlurringCanvas的底图和mBlurredBitmap,并会在被模糊视图的大小改变时重新创建它们。同时,我们也需要重新创建RenderScript的Allocation对象也就是mBlurInputmBlurOutput

第四,为了设计的明亮程度考虑,当最上面的被模糊视图拥有属性PorterDuff.Mode.OVERLAY时我们也可以绘制一个统一白色半透明层。

最后,由于RenderScript仅在API版本17及以上有效,我们在较低级版本也应该有个比较优雅的降级方案。可不幸的是,正如Nicholas Pomepuy的博文中说的那样,通过Java来实现图片模糊渲染速度上达不到实时渲染的需求。最后我们只能决定使用个有较高透明度的半透明视图做为降级方案。

优缺点

我们欣赏这个视图的绘制策略因为它能做到实时模糊同时十分简单易用。它无需知道被模糊视图的内容,同时在模糊和被模糊视图的关系之间有很大的灵活性。当然,最重要的是他很好地满足了我们的需求。

然而,这种策略需要模糊视图通过适当的坐标转换来掌握被模糊视图的位置。关键是模糊视图不能是被模糊视图的子视图否则你将会收到堆栈溢出错误提示因为它们在互相调用对方的绘制方法。一个简单但又十分有效摆脱这种限制的方法是保证模糊视图是被模糊视图的姊妹视图并通过z-order来变换它们的层次关系。

还有个注意点是对于矢量图和文本,默认的位图采样并不太有效。

类库和演示

你可以在我们的Android应用中看到完全的解决方案。同时我们也在GitHub上推出了一个轻量级的开源类库,里面有个演示应用来展示如何在内容发生改变和视图动画时使用该类库。





原文发布时间为:2016年04月20日


本文来自合作伙伴掘金,了解相关信息可以关注掘金网站。

时间: 2024-11-01 20:50:16

在 Android 下进行实时模糊渲染的相关文章

rtsp-跪求大神指教Android下播放RTSP实时流问题!

问题描述 跪求大神指教Android下播放RTSP实时流问题! 小弟在做一个Android客户端,用于监控电脑端摄像头传过来的流,就是能在Android客户端看到电脑摄像头看到的东西.现在遇到了问题不会解决.1.先用了DarWin来充当流服务器,然后它里面有配置一个端口和地址,然后调用rtsp://218.192.127.43:554/live.sdp电脑上安装一个VLC.然后就可以看到摄像头传来的画面了.2.我想在Android端也实现这样的功能.我用SurfaceView,Mediaplay

Android下SDL2实现五子棋游戏_Android

本文实例介绍了Android下用SDL2实现一个简单的五子棋游戏,分享给大家供大家参考,具体内容如下 1. Five.c // Five.c // SDL2 五子棋 // gcc -mwindows -o Five Five.c FiveData.c FiveData.h -lSDL2 -lSDL2main -lSDL2_image -lSDL2_ttf //#define _DEBUG_ #include <stdio.h> #include <string.h> #includ

Android下2d物理引擎Box2d用法简单实例_Android

本文实例讲述了Android下2d物理引擎Box2d用法.分享给大家供大家参考.具体如下: 程序运行的时候需要加载Jbox2d的库,可到以下地址下载(使用的是不带渲染部分的库jbox2d-2.0.1-library-only.jar): http://sourceforge.net/projects/jbox2d/ package com.test; import org.jbox2d.collision.AABB; import org.jbox2d.collision.CircleDef;

Android下获取FPS的几种方法

FPS(Frames Per Second)是关乎Android用户体验最为重要的指标之一,而在VR中更是如此.为了评估VR系统.VR SDK及Unity应用的性能,通常会实时获取FPS并将其显示出来. Android下获取FPS的方法有很多种,本文将介绍三种最为简单易行的方法.三种方法的共同点是都无需root,兼容性好,但又各具特色. 第一种方法,使用GameBench,它的特点是图表显示,非常直观.相对其他跑分软件,它测出的数据更为客观准确.GameBench只能监测前台应用的FPS,无法获

Android下2d物理引擎Box2d用法简单实例

本文实例讲述了Android下2d物理引擎Box2d用法.分享给大家供大家参考.具体如下: 程序运行的时候需要加载Jbox2d的库,可到以下地址下载(使用的是不带渲染部分的库jbox2d-2.0.1-library-only.jar): http://sourceforge.net/projects/jbox2d/ package com.test; import org.jbox2d.collision.AABB; import org.jbox2d.collision.CircleDef;

布局-android 下拉刷新的实现类

问题描述 android 下拉刷新的实现类 求下拉刷新的实现(刷新的主体不单单是listview一种的,而是包含了图片,listvew等组合在一起的),怎么实现啊,求源码 解决方案 需求:项目中的消息列表界面要求实现类似sina微博的下拉刷新:思路:一般的消息列表为ListView类型,将list加载到adapter中,再将adapter加载到ListView中,从而实现消息列表的展示.而下拉刷新要求给消息列表加一个头部,其中有图片(向上/向下箭头)和提示字样(下拉刷新/松开刷新),从而我们需要

android 代码怎么实时监控连接当前wifi热点的设备的连接或断开事件?

问题描述 android 代码怎么实时监控连接当前wifi热点的设备的连接或断开事件? 如题,手机A开了热点,手机B打开Wifi连接上了A.那么当B关闭Wifi按钮时,A怎么实时感知到.有没有实现的代码.方法.求大神们解救哦哦!!!!! 解决方案 // 当网络变化的时候系统会发出一个广播 Broadcast,只要在程序中注册一个广播接收器 BroadcastReceiver,并在 IntentFilter 中添加相应的过滤,这样一旦网络有变化,程序就能监听到 public static fina

Android下如何使用百度地图sdk

百度地图 Android SDK是一套基于Android 2.1(v1.3.5及以前版本支持android 1.5以上系统)及以上版本设备的应用程序接口   可以使用该套 SDK开发适用于Android系统移动设备的地图应用,通过调用地图SDK接口,您可以轻松访问百度地图服务和数据,构建功能丰富.交互性强的LBS(地图类)应用程序. 百度地图Android SDK提供的所有服务是免费的,接口使用无次数限制.您需申请密钥(key)后,才可使用百度地图Android SDK.任何非营利性产品请直接使

windows8.1系统在高分屏下软件界面显示模糊怎么办

  1.首先在桌面空白处右键,点击个性化;然后点击显示; 2.然后勾选让我选择一个适合我的所有显示器的缩放级别,再选中较小-100%,点击应用,在弹出的窗口中选择选择立即注销. 3.再看到软件界面和图标都清晰显示了,不过字体和图标变小了,不要担心,这时再改变刚才选择的缩放等级为 中等 -125%,同样注销后,就都显示正常了. win8.1系统在高分屏下软件界面显示模糊的话,可以参考上述方法解决,简单实用的小技巧.