Android利用RenderScript实现毛玻璃模糊效果示例

RenderScript 介绍

在开始之前,先看下 RenderScript 的官方介绍:

RenderScript is a framework for running computationally intensive tasks at high performance on Android. RenderScript is primarily oriented for use with data-parallel computation, although serial workloads can benefit as well. The RenderScript runtime parallelizes work across processors available on a device, such as multi-core CPUs and GPUs. This allows you to focus on expressing algorithms rather than scheduling work. RenderScript is especially useful for applications performing image processing, computational photography, or computer vision.

大致意思就是说 RenderScript 是 Android 平台上为了计算密集型任务的一中高性能框架,并且RenderScript 尤其对图像的处理特别有用。另外,RenderScript 之所以效率高是因为其底层是 C 实现的。

使用 RenderScript Support Library

为了可以兼容到更早的版本,我们直接使用 android.support.v8.renderscript(支持API level 9+)包下的,而不使用android.renderscript(支持API level 11+)

以 Android Studio 为例,打开你的 app 的 build.gradle 文件,在 android 的 defaultConfig 结点添加两句:

renderscriptTargetApi 18 renderscriptSupportModeEnabled true

其中 renderscriptTargetApi 的值官方说的是从 11 到最新的 API Level 都可以

这样我们等下导包就可以导 v8 内的了。

模糊背景

局部模糊

先上一张我们要实现的效果图:

这里可以看到实现的是局部模糊,在图片的正中间有一个 TextView,TextView 的背景部分做了模糊处理。

先大致说下模糊的主要步骤(完全模糊步骤一样):

首先取出 TextView 在 ImageView 正上方处的那一块背景 然后对取出的那一块背景做模糊处理 最后把模糊处理后的背景再设为 TextView 的背景

这样,就可以达到我们图片中的局部模糊效果,具体的过程在代码中有详细的注释。

下面先贴上布局文件:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="20dp"> <FrameLayout android:layout_width="300dp" android:layout_height="300dp" android:layout_centerInParent="true"> <ImageView android:id="@+id/image" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" android:src="@drawable/img"/> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Melody" android:textColor="@android:color/white" android:textSize="45sp"/> </FrameLayout> </RelativeLayout>

再贴上java代码:

public class MainActivity extends Activity implements Runnable { private static final String TAG = "MainActivity"; private ImageView mImage; private TextView mText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mImage = (ImageView) findViewById(R.id.image); mText = (TextView) findViewById(R.id.text); // onCreate()内无法到ImageView的背景,所以需要 post 到消息队列内稍后执行 mImage.post(this); } @Override public void run() { blur(getImageViewBitmap(mImage), mText); } /** * 取出一个imageView的bitmap背景 */ public Bitmap getImageViewBitmap(ImageView imageView) { imageView.setDrawingCacheEnabled(true); // 取出ImageView的Bitmap Bitmap bitmap = imageView.getDrawingCache(); // 拷贝一份bitmap用作模糊 Bitmap bitmapCopy = bitmap.copy(bitmap.getConfig(), true); imageView.setDrawingCacheEnabled(false); return bitmapCopy; } /** * 模糊的具体实现 * * @param inputBitmap 要模糊的 bitmap * @param targetView 需要被模糊背景的 View */ public void blur(Bitmap inputBitmap, View targetView) { // 创建一个和目标View(需要背景被模糊的View)宽高一样的空的 outputBitmap Bitmap outputBitmap = Bitmap.createBitmap((int) (targetView.getMeasuredWidth()), (int) (targetView.getMeasuredHeight()), Bitmap.Config.ARGB_8888); // 将 outputBitmap 关联在 canvas 上 Canvas canvas = new Canvas(outputBitmap); // 画布移动到目标 View 在父布局中的位置 canvas.translate(-targetView.getLeft(), -targetView.getTop()); Paint paint = new Paint(); paint.setFlags(Paint.FILTER_BITMAP_FLAG); // 将要模糊的 inputBitmap 绘制到 outputBitmap 上 // 因为刚才做了 translate 操作,这样就可以裁剪到目标 View 在父布局内的那一块背景到 outputBitmap 上 canvas.drawBitmap(inputBitmap, 0, 0, paint); // ----接下来做模糊 outputBitmap 处理操作---- // 创建 RenderScript RenderScript rs = RenderScript.create(this); Allocation input = Allocation.createFromBitmap(rs, outputBitmap); Allocation output = Allocation.createTyped(rs, input.getType()); // 使用 ScriptIntrinsicBlur 类来模糊图片 ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create( rs, Element.U8_4(rs)); // 设置模糊半径 ( 取值范围为( 0.0f , 25f ] ,半径越大,模糊效果也越大) blur.setRadius(25f); blur.setInput(input); // 模糊计算 blur.forEach(output); // 模糊 outputBitmap output.copyTo(outputBitmap); // 将模糊后的 outputBitmap 设为目标 View 的背景 targetView.setBackground(new BitmapDrawable(getResources(), outputBitmap)); rs.destroy(); } }

导的是 v8 的包:

import android.support.v8.renderscript.Allocation; import android.support.v8.renderscript.Element; import android.support.v8.renderscript.RenderScript; import android.support.v8.renderscript.ScriptIntrinsicBlur;

完全模糊

上面是局部模糊,然后我们改变一下 TextView 的宽高铺满父布局,使其和 ImageView 大小一样来实现完全模糊效果:

... <TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="Melody" android:textColor="@android:color/white" android:textSize="45sp"/> ...

java代码部分不需要改变,下面再看效果图:

效率如何?

为了查看操作耗时,我使用 Log 在 blur() 方法的开头和结束地方分别计算了时间,然后查看时间差:

10-09 17:04:23.664 23665-23665/com.melodyxxx.blurdemo2 E/MainActivity: spend: 120ms

可以看到居然花了 120ms,显然效率不够高,有没有优化的方法?(测试机型为 魅族 PRO 6)

效率优化

上面的是直接将原 ImageView 的 bitmap 直接模糊处理,效率不够高,所以我们可以先将原图片进行压缩处理,然后在进行模糊,下面为关键代码,scaleFactor 为压缩比例大小,例如 scaleFactor 为 2,代表先将原图压缩为原来的 1/2,然后进行模糊,效率会高很多。

... Bitmap outputBitmap = Bitmap.createBitmap((int) (mTargetView.getMeasuredWidth() / scaleFactor), (int) (mTargetView.getMeasuredHeight() / scaleFactor), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(outputBitmap); canvas.translate(-mTargetView.getLeft() / scaleFactor, -mTargetView.getTop() / scaleFactor); canvas.scale(1 / scaleFactor, 1 / scaleFactor); Paint paint = new Paint(); paint.setFlags(Paint.FILTER_BITMAP_FLAG); canvas.drawBitmap(mInputBitmap, 0, 0, paint); ...

下面是 Demo 效果图:

结果:

压缩比例为 2 时:耗时 19 ms

压缩比例为 8 时:耗时 2 ms

根据压缩比例配合不同的模糊半径可以达到不同模糊效果。

再来看 Demo 效果图中拖动 SeekBar 可以动态的实现模糊效果,首先想到的方法是每次拖动时实时计算模糊,这样的效率肯定不行,还会造成卡顿,我这里的方法是先将图片最大化模糊一次设给上方 ImageView 的背景即可,然后 SeekBar 拖动时,只需要改变最上方或者下方图片的透明度就可以达到上面的效果。

Demo apk 下载:http://fir.im/snmb

Demo 地址:点击这里

总结

除了 RenderScript 以外还有一些其他的方法也可以实现高斯模糊,例如 FastBlur 等,在模糊的时候不要将原图直接模糊处理,可采取先缩放然后再模糊,这样可以大大提高模糊的速度。好了,以上就是这篇文章的全部内容了,希望本文的内容对各位开发者能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

时间: 2024-10-10 19:33:21

Android利用RenderScript实现毛玻璃模糊效果示例的相关文章

Android开发之实现毛玻璃模糊效果

手机API等级在16以上的,可以用这个方法: if (VERSION.SDK_INT > 16) {     Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);     final RenderScript rs = RenderScript.create(context);     final Allocation input = Allocation.createFromBitmap(rs, sentBitmap, A

Android编程之View简单学习示例_Android

本文实例讲述了Android编程之View简单学习示例.分享给大家供大家参考,具体如下: View,是Android的一个超类,这个类几乎包含了所有的屏幕类型.每一个View都有一个用于绘图的画布,这个画布可以进行任意扩展. 在游戏开发中叶可以自定义视图(View),这个画布的功能更能满足我们在游戏开发中的需要.在Android中,任何一个View类都只需重写onDraw 方法来实现界面显示,自定义的视图可以是复杂的3D实现,也可以是非常简单的文本形式等. 游戏的核心是不断地绘图和刷新界面,An

Android编程之View简单学习示例

本文实例讲述了Android编程之View简单学习示例.分享给大家供大家参考,具体如下: View,是Android的一个超类,这个类几乎包含了所有的屏幕类型.每一个View都有一个用于绘图的画布,这个画布可以进行任意扩展. 在游戏开发中叶可以自定义视图(View),这个画布的功能更能满足我们在游戏开发中的需要.在Android中,任何一个View类都只需重写onDraw 方法来实现界面显示,自定义的视图可以是复杂的3D实现,也可以是非常简单的文本形式等. 游戏的核心是不断地绘图和刷新界面,An

udp-关于Android利用局域网进行UDP通信的问题

问题描述 关于Android利用局域网进行UDP通信的问题 我写了一个小程序准备利用UDP和电脑之间进行通信,然后基于这个做一点东西,可是问题是我用AVD测试很好用的,但是用真机测试就是不行.我的思路是让电脑和Android设备连接一台路由器,路由器为两台设备分配固定的ip地址进行通信.拜托大家帮我看看啊,我这才是第一步就郁闷的要死下面是代码.1:MainActivity package com.company.zebork.testudpll; import android.app.Activ

Android 利用mediarecorder录制视频后用ThumbnailUtils类切图返回空

问题描述 Android 利用mediarecorder录制视频后用ThumbnailUtils类切图返回空 如题,因为兼容性问题,将录制时候的MediaRecorder参数如下: recorder = new MediaRecorder(); recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); //指定OutputFo

Android Dialog 对话框详解及示例代码_Android

Android Dialog 对话框 1.Dialog介绍 2.AlertDialog的基本使用 3.自定义对话框 Custom Dialog 一.Dialog介绍 Dialog也是Android中常用的用户界面元素,他同Menu一样也不是View的子类.让我们看一下它的继承关系: 这里要留意一下他的直接子类 AlertDialog,和间接子类 DatePickerDialog,ProgressDialog,TimePickerDialog,其中后三个我们在前面的章节已经讲过,今天我们把重点放在

android利用Zxing扫码后的声音如改变

问题描述 android利用Zxing扫码后的声音如改变 现在的项目里需要两种不同的声音 扫码成功失败各需一种 请问如何修改呢? 解决方案 http://blog.csdn.net/xiaanming/article/details/10163203

我的Android进阶之旅------&amp;gt;Android利用Sensor(传感器)实现指南针功能

首先来说一说该指南针的实现思路: 程序先准备一张指南针图片,该图片上方向指针指向北方.接下来开发一个检测方向的传感器,程序检测到手机顶部绕Z轴转过多少度,让指南针图片反向转多少度即可.由此可见指南针应用只要在界面中添加一张图片,并让图片总是反向转过反向传感器返回的第一个角度值即可. 下面介绍一下方向传感器:方向传感器用于感应手机设备的摆放状态.方向传感器可以返回三个角度,这三个角度即可确定手机的摆放状态.关于方向传感器返回的三个角度的说明如下. 第一个角度:表示手机顶部朝向正北方的夹角.当手机绕

我的Android进阶之旅------&amp;gt;Android利用Sensor(传感器)实现水平仪功能的小例

       这里介绍的水平仪,指的是比较传统的气泡水平仪,在一个透明圆盘内充满液体,液体中留有一个气泡,当一端翘起时,该气泡就会浮向翘起的一端.    利用方向传感器返回的第一个参数,实现了一个指南针小应用.    我的Android进阶之旅------>Android利用Sensor(传感器)实现指南针功能  (地址:http://blog.csdn.net/ouyang_peng/article/details/8801204)    接下来,我们利用返回的第二.三个参数实现该水平仪.因为