安卓的模糊视图

发布于 2024-07-25 20:14:27 字数 5039 浏览 24 评论 0

模糊效果可以生动地表现出内容的层次感,当使用者关注重点内容,即便在模糊表面之下发生视差效果或者动态改变,也能够保持当前背景。

在 IOS 设备中,我们首先构造一个 UIVisualEffectView,之后添加 visualEffectView 到 view 层,在 view 中可以进行动态的模糊。

UIVisualEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];

UIVisualEffectView *visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];

安卓中的表现形式

我们在雅虎天气 APP 中确实看到了很好的模糊效果实例,但是根据 Nicholas Pomepuy 的博客帖子 ,然而,这个 App 是通过缓存一张预渲染的背景图片来实现图片虚化的。

虽然这种方法非常有效,但是确实不符合我们的需求,在 500px 的 APP 中,图像通常是获得焦点的内容,而不仅仅是提供背景,这说明图像的变化很大且迅速,即便他们是在模糊层之下。在我们的 安卓 APP 中即是一个恰当的例子:当用户滑动至下一页时,整排图片会以相反方向淡出,为了组成所需的模糊效果,适当地管理多个预渲染图是困难的。

一种绘制模糊视图的方法

我们需要的效果是,实时地模糊其下的视图,最终需要给界面的是模糊视图的一个 blurred view 引用。

blurringView.setBlurredView(blurredView);

之后当 blurred view 改变时,不管是因为内容改变(比如呈现新的图片)、view 的变换还是处在动画过程,我们都要刷新 blurring view。

blurringView.invalidate();

为了实现 blurring view, 我们需要继承 view 类并重写 onDraw() 方法来渲染模糊效果。

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

    // Use the blurred view’s draw() method to draw on a private canvas.
    mBlurredView.draw(mBlurringCanvas);

    // Blur the bitmap backing the private canvas into mBlurredBitmap
    blur();

    // Draw mBlurredBitmap with transformations on the blurring view’s main canvas.
    canvas.save();
    canvas.translate(mBlurredView.getX() - getX(), mBlurredView.getY() - getY());
    canvas.scale(DOWNSAMPLE_FACTOR, DOWNSAMPLE_FACTOR);
    canvas.drawBitmap(mBlurredBitmap, 0, 0, null);
    canvas.restore();
}

这里的关键点在于,当模糊视图重绘时,它使用 blurred view 的 draw() 方法,模糊视图保持 blurred view 的引用,它绘制一个私有的,以 bitmap 作为背景的画布。

mBlurredView.draw(mBlurringCanvas);

这种使用另一个视图的 draw()方法,也适用于建立放大或者个性的 UI 界面,在其中,放大区域的内容是扩大的,而不是模糊的。

根据 Nicholas Pomepuy 讨论 的想法,我们使用子采样和 渲染脚本 进行快速绘制,当初始化 blurring view 的私有画布 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);

通过 mBlurringCanvas 的 建立与恰当的渲染脚本初始化,重绘时的 blur() 方法如下:

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

此时 mBlurredBitmap 已准备好,剩下的就是使用适当的变换和缩放,在 blurring view 自己的画布上重绘视图。

实现细节

完整实现 blurring view 时,我们需要注意几个技术点:

一:我们发现缩放因子 8,模糊半径 15 很好地满足我们的目标,但满足你需求的参数可能是不同的。

二:在模糊的 bitmap 边缘会遇到一些渲染脚本效果,我们将缩放的宽度和高度进行了圆角化,直到最近的 4 的倍数。

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

三:为了保证更好的表现效果,我们新建两个 bitmap 对象——mBitmapToBlur 和 mBlurredBitmap ,
mBitmapToBlur 位于私有画布 mBlurringCanvas 之下, mBlurredBitmap 仅当 blurred view 的大小变化时才重新建立他们;
同样地当 blurred view 的大小改变时,我们才创建渲染脚本对象 mBlurInput 和 mBlurOutput。

四:我们以 with PorterDuff.Mode.OVERLAY 模式绘制一个白色半透明的层,它处在被模糊的的图片上层。用来处理设计需求中的淡化。

最后,因为 RenderScript(渲染脚本)至少在 API 17 上才可用,我们需要降低旧版本的安卓,糟糕的是, Nicholas Pomepuy 帖子 中提到的 Java bitmap 的模糊方法,当恰当地预渲染缓存副本时,对于实时渲染不够迅速,我们所做的是使用一个半透明的 view 作为回调。(更新于 2015 年 3 月 23 日:通过使用 RenderScript 库 ,我的解决方法能在更低版本的 API 中运行。下面提及的库和 Demo 都更新了,非常感谢 GitHub 上的小伙伴 panzerdev 告诉我这一点)

优点和缺点

我们喜欢这个 view 的绘制方法,因为它可以实时模糊并且容易使用,使得 blurred view 的内容,也在 blurring view 和 blurred view 中间保证了灵活性,最重要的是,它满足了我们的需求

这个方法确实使得 blurring view 与适当协同变换的 blurred view 保持了私有联系,相关地,模糊视图必须不能是 blurred view 的子类,否则会因为互相调用造成堆栈溢出。简单有效处理此限制的方法是要保证模糊视图与 blurred view 在同级,并且 Z 轴次序上 blurred view 在 blurring view 之前。

另一点需要注意的限制是,由于与 矢量图形和文本 有关,我们默认的 bitmap 削减采样表现效果不是很好。

库文件和示例

你可以在我们的安卓 APP 上看到解决方法,我们也把开源的库文件连同一个示例分享到了 github ,它能够展示内容变换、动画和视图变换。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

ㄟ。诗瑗

暂无简介

文章
评论
27 人气
更多

推荐作者

夢野间

文章 0 评论 0

百度③文鱼

文章 0 评论 0

小草泠泠

文章 0 评论 0

zhuwenyan

文章 0 评论 0

weirdo

文章 0 评论 0

坚持沉默

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文