Android 动态高斯模糊效果教程_Android

写在前面

最近一直在做毕设项目的准备工作,考虑到可能要用到一个模糊的效果,所以就学习了一些高斯模糊效果的实现。比较有名的就是 FastBlur 以及它衍生的一些优化方案,还有就是今天要说的RenderScript 。

因为这东西是现在需要才去学习的,所以关于一些图像处理和渲染问题就不提了。不过在使用的过程中确实能感受到,虽然不同的方案都能实现相同的模糊效果,但是效率差别真的很大。

本篇文章实现的高斯模糊是根据下面这篇文章学习的,先推荐一下。本文内容与其内容差不多,只是稍微讲的详细一点,并修改了代码中部分实现逻辑和细节上的处理。不过主体内容不变,所以选择哪篇文章去学都是一样的。

下面就来看一下,如何去实现这样的高斯模糊效果。

简单聊聊 Renderscript

因为效果的实现是基于 Renderscript 的,所以有必要先来了解一下。

从它的官方文档来看,说的很是玄乎。我们只需要知道一点就好了:

RenderScript is a framework for running computationally intensive tasks at high performance on Android.
Renderscript 是 Android 平台上进行高性能计算的框架。
既然是高性能计算,那么说明 RenderScript 对图像的处理非常强大,所以用它来实现高斯模糊还是比较好的选择。

那么如何使用它呢?从官方文档中可以看到,如果需要在 Java 代码中使用 Renderscript 的话,就必须依赖 android.renderscript 或者android.support.v8.renderscript 中的 API 。既然有 API 那就好办多了。

下面简单说一下使用的步骤,这也是官方文档中的说明:

  1. 首先需要通过 Context 创建一个 Renderscript ;
  2. 其次通过创建的 Renderscript 来创建一个自己需要的脚本( ScriptIntrinsic ),比如这里需要模糊,那就是 ScriptIntrinsicBlur ;
  3. 然后至少创建一个 Allocation 类来创建、分配内存空间;
  4. 接着就是对图像进行一些处理,比如说模糊处理;
  5. 处理完成后,需要刚才的 Allocation 类来填充分配好的内存空间;
  6. 最后可以选择性的对一些资源进行回收。

文档中的解释永远很规矩,比较难懂,我们结合原博主 湫水长天 的代码来看一看步骤:

/**
 * @author Qiushui
 * @description 模糊图片工具类
 * @revision Xiarui 16.09.05
 */
public class BlurBitmapUtil {
  //图片缩放比例
  private static final float BITMAP_SCALE = 0.4f;

  /**
   * 模糊图片的具体方法
   *
   * @param context 上下文对象
   * @param image  需要模糊的图片
   * @return 模糊处理后的图片
   */
  public static Bitmap blurBitmap(Context context, Bitmap image,float blurRadius) {
    // 计算图片缩小后的长宽
    int width = Math.round(image.getWidth() * BITMAP_SCALE);
    int height = Math.round(image.getHeight() * BITMAP_SCALE);

    // 将缩小后的图片做为预渲染的图片
    Bitmap inputBitmap = Bitmap.createScaledBitmap(image, width, height, false);
    // 创建一张渲染后的输出图片
    Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);

    // 创建RenderScript内核对象
    RenderScript rs = RenderScript.create(context);
    // 创建一个模糊效果的RenderScript的工具对象
    ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));

    // 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间
    // 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去
    Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
    Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);

    // 设置渲染的模糊程度, 25f是最大模糊度
    blurScript.setRadius(blurRadius);
    // 设置blurScript对象的输入内存
    blurScript.setInput(tmpIn);
    // 将输出数据保存到输出内存中
    blurScript.forEach(tmpOut);

    // 将数据填充到Allocation中
    tmpOut.copyTo(outputBitmap);

    return outputBitmap;
  }
}

上面就是处理高斯模糊的代码,其中注释写的十分详细,而且已经将图片缩放处理了一下。结合刚才说的步骤,大家应该能有一个大概的印象,实在不懂也没关系,这是一个工具类,直接 Copy 过来即可。

当然,原博主将代码封装成轮子了,也可以直接在项目中引用 Gradle 也是可以的,但是我觉得源码还是要看一看的。

简单的模糊

好了,有了一个大概的印象后,来看一下如何实现高斯模糊效果吧!

首先你可以在项目中直接引用原博主封装的轮子:

compile 'com.qiushui:blurredview:0.8.1'

如果不想引用的话,就必须在当前 Module 的 build.gradle 中添加如下代码:

defaultConfig {
  renderscriptTargetApi 19
  renderscriptSupportModeEnabled true
}

等构建好就可以使用了。如果构建失败的话,只需要把 minSdkVersion 设置成 19 就好了,暂时不知是何原因。不过从 StackOverflow 中了解到这是个Bug ,那就不必深究。

现在来看代码实现,首先布局文件中就一个 ImageView ,没啥好说的,从上面的模糊图片工具类可以看出,要想获得一个高斯模糊效果的图片,需要三样东西:

Context:上下文对象
Bitmap:需要模糊的图片
BlurRadius:模糊程度

这里需要注意一下:

目前这种方案只适用于 PNG 格式的图片,而且图片大小最好小一点,虽然代码中已经缩放了图片,但仍然可能会出现卡顿的情况。

现在只要设置一下图片和模糊程度就好了:

/**
 * 初始化View
 */
@SuppressWarnings("deprecation")
private void initView() {
  basicImage = (ImageView) findViewById(R.id.iv_basic_pic);
  //拿到初始图
  Bitmap initBitmap = BitmapUtil.drawableToBitmap(getResources().getDrawable(R.raw.pic));
  //处理得到模糊效果的图
  Bitmap blurBitmap = BlurBitmapUtil.blurBitmap(this, initBitmap, 20f);
  basicImage.setImageBitmap(blurBitmap);
}

来看一下运行图:

可以看到,图片已经实现了模糊效果,而且速度还蛮快的,总的来说通过 BlurBitmapUtil.blurBitmap()就能得到一张模糊效果的图 。

自定义模糊控件

原博主的轮子里给我们封装了一个自定义的 BlurredView ,刚开始我觉得没必要自定义。后来发现自定义的原因是需要实现动态模糊效果。

那为什么不能手动去设置模糊程度呢?他给出的解释是:

“如果使用上面的代码进行实时渲染的话,会造成界面严重的卡顿。”

我也亲自试了一试,确实有点卡。他实现动态模糊处理的方案是这样的:

“先将图片进行最大程度的模糊处理,再将原图放置在模糊后的图片上面,通过不断改变原图的透明度(Alpha值)来实现动态模糊效果。”

这个方案确实很巧妙的实现动态效果,但是注意如果要使用这种方式,就必须有两张一模一样的图片。如果在代码中直接写,就需要两个控件,如果图片多的话,显然是不可取的。所以轮子里有一个自定义的 BlurredView 。

不过这个 BlurredView 封装的不是太好,我删减了一部分内容,原因稍后再说。先来看一下核心代码。

首先是自定义的 BlurredView 继承于 RelativeLayout ,在布局文件中可以看到,里面有两个 ImageView,且是叠在一起的。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

  <ImageView
    android:id="@+id/blurredview_blurred_img"
    .../>

  <ImageView
    android:id="@+id/blurredview_origin_img"
    .../>

</FrameLayout>

同时也定义了一些属性:

<resources>
  <declare-styleable name="BlurredView">
    <attr name="src" format="reference"/>
    <attr name="disableBlurred" format="boolean"/>
  </declare-styleable>
</resources>

一个是设置图片,一个是设置是否禁用模糊。最后就是 BlurredView 类,代码如下,有大量删减,只贴出核心代码:

/**
 * @author Qiushui
 * @description 自定义模糊View类
 * @revision Xiarui 16.09.05
 */
public class BlurredView extends RelativeLayout {

  /*========== 全局相关 ==========*/
  private Context mContext;//上下文对象
  private static final int ALPHA_MAX_VALUE = 255;//透明最大值
  private static final float BLUR_RADIUS = 25f;//最大模糊度(在0.0到25.0之间)

  /*========== 图片相关 ==========*/
  private ImageView mOriginImg;//原图ImageView
  private ImageView mBlurredImg;//模糊后的ImageView
  private Bitmap mBlurredBitmap;//模糊后的Bitmap
  private Bitmap mOriginBitmap;//原图Bitmap

  /*========== 属性相关 ==========*/
  private boolean isDisableBlurred;//是否禁用模糊效果

  ...

  /**
   * 以代码的方式添加待模糊的图片
   *
   * @param blurredBitmap 待模糊的图片
   */
  public void setBlurredImg(Bitmap blurredBitmap) {
    if (null != blurredBitmap) {
      mOriginBitmap = blurredBitmap;
      mBlurredBitmap = BlurBitmapUtil.blurBitmap(mContext, blurredBitmap, BLUR_RADIUS);
      setImageView();
    }
  }
  ...

  /**
   * 填充ImageView
   */
  private void setImageView() {
    mBlurredImg.setImageBitmap(mBlurredBitmap);
    mOriginImg.setImageBitmap(mOriginBitmap);
  }

  /**
   * 设置模糊程度
   *
   * @param level 模糊程度, 数值在 0~100 之间.
   */
  @SuppressWarnings("deprecation")
  public void setBlurredLevel(int level) {
    //超过模糊级别范围 直接抛异常
    if (level < 0 || level > 100) {
      throw new IllegalStateException("No validate level, the value must be 0~100");
    }

    //禁用模糊直接返回
    if (isDisableBlurred) {
      return;
    }

    //设置透明度
    mOriginImg.setAlpha((int) (ALPHA_MAX_VALUE - level * 2.55));
  }

  ...
}

从代码中可以看到,最核心的就是下面三个方法:

setBlurredImg(Bitmap blurredBitmap):设置图片,并复制两份;
setImageView():给两个ImageView设置相应的图片,内部调用;
setBlurredLevel(int level):设置透明程度;

思路就是先选定一张图片,一张作为原图,一张作为模糊处理过的图。再分别将这两张图设置给自定义 BlurredView 中的两个 ImageView ,最后处理模糊过后的那张图的透明度。
好了,现在来写一个自定义的模糊效果图,首先是布局,很简单:

 <com.blurdemo.view.BlurredView
    android:id="@+id/bv_custom_blur"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:src="@raw/pic"
    app:disableBlurred="false" />

可以看到,设置了图片,设置了开启模糊,那么我们在Activity中只需设置透明程度即可:

private void initView() {
    customBView = (BlurredView) findViewById(R.id.bv_custom_blur);
    //设置模糊度
    customBView.setBlurredLevel(100);
  }

效果图与上图一样,这里就不重复贴了。可以看到,代码简单了很多,不过仅仅因为方便简单可不是自定义 View 的作用,作用在于接下来要说的 动态模糊效果 的实现。

动态模糊

我们先来看一下啥叫动态模糊效果:

从图中可以看到,随着我们触摸屏幕的时候,背景的模糊程度会跟着变化。如果要直接设置其模糊度会及其的卡顿,所以正如原博主所说,可以用两张图片来实现。

大体思路就是,上面的图片模糊处理,下面的图片不处理,然后通过手势改变上面模糊图片的透明度即可。

所以跟前面的代码几乎一样,只需要重写 onTouchEvent 方法即可:

/**
 * 初始化View
 */
private void initView() {
  customBView = (BlurredView) findViewById(R.id.bv_dynamic_blur);
  //设置初始模糊度
  initLevel = 100;
  customBView.setBlurredLevel(initLevel);
}

/**
 * 触摸事件
 */
@Override
public boolean onTouchEvent(MotionEvent ev) {
  switch (ev.getAction()) {
    case MotionEvent.ACTION_DOWN:
      downY = ev.getY();
      break;

    case MotionEvent.ACTION_MOVE:
      float moveY = ev.getY();
      //手指滑动距离
      float offsetY = moveY - downY;
      //屏幕高度 十倍是为了看出展示效果
      int screenY = getWindowManager().getDefaultDisplay().getHeight() * 10;
      //手指滑动距离占屏幕的百分比
      movePercent = offsetY / screenY;
      currentLevel = initLevel + (int) (movePercent * 100);
      if (currentLevel < 0) {
        currentLevel = 0;
      }
      if (currentLevel > 100) {
        currentLevel = 100;
      }
      //设置模糊度
      customBView.setBlurredLevel(currentLevel);
      //更改初始模糊等级
      initLevel = currentLevel;
      break;
    case MotionEvent.ACTION_UP:
      break;
  }
  return super.onTouchEvent(ev);
}

从代码中可以看到,这里是通过手指滑动距离占屏幕的百分比来计算改变后的透明等级的,代码应该不难,很容易理解。当然原博主博客中是通过进度条来改变的,也是可以的,就不在赘述了。

与 RecylcerView 相结合

先来看一张效果图,这个图也是仿照原博主去实现的,但是还是有略微的不同。

本来的自定义 BlurredView 中还有几段代码是改变背景图的位置的,因为希望上拉下拉的时候背景图也是可以移动的,但是从体验来看效果不是太好,上拉的过程中会出现留白的问题。

虽然原博主给出了解决方案:手动给背景图增加一个高度,但这并不是最好的解决方式,所以我就此功能给删去了,等找到更好的实现方式再来补充。

现在来看如何实现?首先布局就是底下一层自定义的 BlurredView ,上面一个 RecylcerView,RecylcerView 有两个 Type ,一个是头布局,一个是底下的列表,很简单,就不详细说了。

重点仍然是动态模糊的实现,在上面的动态模糊中,我们采取了重写 onTouchEvent 方法,但是这里刚好是 RecylcerView ,我们可以根据它的滚动监听,也就是 onScrollListener 来完成动态改变透明度,核心方法如下:

 //RecyclerView 滚动监听
  mainRView.setOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
      super.onScrollStateChanged(recyclerView, newState);
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
      super.onScrolled(recyclerView, dx, dy);
      //滚动距离
      mScrollerY += dy;
      //根据滚动距离控制模糊程度 滚动距离是模糊程度的十倍
      if (Math.abs(mScrollerY) > 1000) {
        mAlpha = 100;
      } else {
        mAlpha = Math.abs(mScrollerY) / 10;
      }
      //设置透明度等级
      recyclerBView.setBlurredLevel(mAlpha);
    }
  });

代码很简单,就是在 onScrolled 方法中计算并动态改变透明度,只要掌握了原理,实现起来还是很容易的。

总结

从前面所有的动态图可以看到,运行起来还是比较快的,但是我从 Android Monitor 中看到,在每一次刚开始渲染模糊的时候,GPU 渲染的时间都很长,所以说可能在性能方面还是有所欠佳。

当然也可能跟模拟器有关系,真机上测试是很快的。而且貌似比 FastBlur 还快一点,等有空测试几个高斯模糊实现方法的性能,来对比一下。

到此,这种实现高斯模糊的方法已经全部讲完了,感谢原博主这么优秀的文章,再次附上链接:

湫水长天 – 教你一分钟实现动态模糊效果

其他参考资料

RenderScript – Android Developers

Android RenderScript入门(1)

高斯模糊效果实现方案及性能对比 – lcyFox

项目源码

BlurDemo – IamXiaRui – Github

以上就是对Android 动态高斯模糊效果教程的示例,谢谢大家对本站的支持!

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
android 动态高斯模糊、android 高斯模糊效果、android 动态模糊效果、动态高斯模糊、安卓动态高斯模糊,以便于您获取更多的相关知识。

时间: 2024-09-20 19:34:20

Android 动态高斯模糊效果教程_Android的相关文章

Android 实现高斯模糊效果且兼容低版本_Android

 一.效果演示 项目中用到了高斯模糊效果,查阅过一些资料,考虑到性能问题最终还是选择使用Android自带的RenderScript库来实现,关于使用RenderScript来实现高斯模糊网上也有很多类似的方法,大部分都总结的比较乱,此处算是做一个整理吧,供有类似需求的同学参考及学习. (项目效果图) 简单描述项目效果图的实现思路: ① 加载定义的xml的Layout ② 使用截屏方法获取当前窗口的Bitmap对象 ③ 将Bitmap对象进行压缩及高斯模糊处理 ④ 将处理过的模糊图对象作为①中所

Android 实现高斯模糊效果且兼容低版本

一.效果演示 项目中用到了高斯模糊效果,查阅过一些资料,考虑到性能问题最终还是选择使用Android自带的RenderScript库来实现,关于使用RenderScript来实现高斯模糊网上也有很多类似的方法,大部分都总结的比较乱,此处算是做一个整理吧,供有类似需求的同学参考及学习. (项目效果图) 简单描述项目效果图的实现思路: ① 加载定义的xml的Layout ② 使用截屏方法获取当前窗口的Bitmap对象 ③ 将Bitmap对象进行压缩及高斯模糊处理 ④ 将处理过的模糊图对象作为①中所加

Android实现动态高斯模糊效果_Android

高斯模糊是什么? 高斯模糊(英语:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop.GIMP以及Paint.NET等图像处理软件中广泛使用的处理效果,通常用它来减少图像噪声以及降低细节层次.这种模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像效果散景以及普通照明阴影中的效果都明显不同. 什么?看不明白?没关系,我也看不明白,维基百科复制回来的嘛.我们直接放一些图片来了解以下这个高斯模糊是怎么样的.因为高斯模糊在iOS中最常见,这里抓

Android动态加载布局_Android

ListView我们一直都在用,只不过当Adapter中的内容比较多的时候我们有时候没办法去设置一些组件,举个例子: 可以看到京东的故事里面的这样一个布局,这个布局可以说是我目前见到的内容比较多的了,它的每一项都包含头像.姓名.分类.内容.图片.喜欢.评论.分享以及喜欢的头像.分析了一下布局之后我们不难发现,除了喜欢头像这部分,其余的都很好实现. 那么下面着重说一下这个头像这部分怎么实现? 第一种方案:我们可以用GridView来实现,GridView和ListView的用法是一样的,俗称九宫格

Android 动态的显示时间_Android

怎么才能动态的实现时间呢?也许刚入行的你不懂.如果不懂得话,请看代码(代码是最好的老师).大笑 public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView=(TextView) findViewById(R.id.time); handler = new Handler() { public void ha

Android实现动态高斯模糊效果

高斯模糊是什么? 高斯模糊(英语:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop.GIMP以及Paint.NET等图像处理软件中广泛使用的处理效果,通常用它来减少图像噪声以及降低细节层次.这种模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像效果散景以及普通照明阴影中的效果都明显不同. 什么?看不明白?没关系,我也看不明白,维基百科复制回来的嘛.我们直接放一些图片来了解以下这个高斯模糊是怎么样的.因为高斯模糊在iOS中最常见,这里抓

Android录音应用实例教程_Android

本文以实例形式较为详细的展示了Android录音的实现方法,分享给大家供大家参考之用.具体方法如下: 首先是xml布局文件: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" andr

仅5步搞定Android开发环境部署 Android开发环境搭建教程_Android

在windows安装Android的开发环境不简单也说不上算复杂,本文写给第一次想在自己Windows上建立Android开发环境投入Android浪潮的朋友们,为了确保大家能顺利完成开发环境的搭建,文章写的尽量详细,希望对准备进入Android开发的朋友有帮助. 本教程将分为五个步骤来完成Android开发环境的部署. 第一步:安装JDK. 第二步:配置Windows上JDK的变量环境 . 第三步: 下载安装Eclipse . 第四步:下载安装Android SDK . 第五步:为Eclips

Android第三方微信支付教程_Android

做了微信支付,下载了Demo,发现和之前有所改动,v3.0的版本,也许有的朋友还在摸索,这里我已经成功支付,话不多说,直接进入主题: 一.首先要在微信开发平台注册账号,新增应用,填写正确的包名,正确的签名(MD5中的一串字符冒号去掉,全部小写),当然也会生成的AppID和AppSecret都有用!最后还要花300大洋开通支付功能! 二.分析一下微信支付的流程 三.界面交互流程 上面都看明白了,接下来就是写代码了 四.这里必须要有wxapi这个包名,同时必须有WXPayEntryActivity这