教你快速实现Android动态模糊效果

前言

雅虎天气的界面上滑的时候背景图片会跟着移动,最重要的是背景图片会根据手指上下移动的距离来进行不同程度的模糊,感觉甚为惊奇,毕竟大家都知道,在Android平台上进行模糊渲染是一个相当耗CPU也相当耗时的操作,一旦处理不好,卡顿是在所难免的。

一般来说,考虑到效率,渲染一张图片最好的方法是使用OpenGL,其次是使用C++/C,使用Java代码是最慢的。但是Android推出RenderScript之后,我们就有了新的选择,测试表明,使用RenderScript的渲染效率和使用C/C++不相上下,但是使用RenderScript却比使用JNI简单地多!同时,Android团队提供了RenderScript的支持库,使得在低版本的Android平台上也能使用。

不过在使用RenderScript之前,对于模糊一张图片,需要注意的是,我们应该尽量不要使用原尺寸分辨率的图片,最好将图片缩小比例,这小渲染的效率要高一些。

动态模糊的实现

如何使用RenderScript来模糊一张图片呢?废话不多说,先上核心代码:

public class BlurBitmap {
 /**
  * 图片缩放比例
  */
 private static final float BITMAP_SCALE = 0.4f;
 /**
  * 最大模糊度(在0.0到25.0之间)
  */
 private static final float BLUR_RADIUS = 25f;

 /**
  * 模糊图片的具体方法
  *
  * @param context 上下文对象
  * @param image  需要模糊的图片
  * @return   模糊处理后的图片
  */
 public static Bitmap blur(Context context, Bitmap image) {
  // 计算图片缩小后的长宽
  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(BLUR_RADIUS);
  // 设置blurScript对象的输入内存
  blurScript.setInput(tmpIn);
  // 将输出数据保存到输出内存中
  blurScript.forEach(tmpOut);

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

  return outputBitmap;
 }
}

完成上面的代码后,需要在app的gradle文件中添加如下的支持:

 defaultConfig {
 ......
 renderscriptTargetApi 19
 renderscriptSupportModeEnabled true
}

代码做了简单的注释以帮助理解,如果需要详细了解,可以查阅官方文档

然后,我们可以看一下模糊前和模糊后的效果对比:

将图片模糊后,接下来要考虑的是怎么实现动态模糊效,有一点需要注意的是,即使我们使用了RenderScript这种高效的渲染方式,但是在实际测试中,渲染一张500*700分辨率的PNG格式图片,在我的Pro 6手机上,仍然需要50ms左右的时间,显然如果使用上面的代码进行实时渲染的话,会造成界面严重的卡顿。

既然实时渲染这条路走不通,那么就需要我们另辟蹊径了,我这里可以提供一种方法:先将图片进行最大程度的模糊处理,再将原图放置在模糊后的图片上面,通过不断改变原图的透明度(Alpha值)来实现动态模糊效果。

简单的代码如下:

public class MainActivity extends AppCompatActivity {

 /**
  * 原始图片控件
  */
 private ImageView mOriginImg;

 /**
  * 模糊后的图片控件
  */
 private ImageView mBluredImage;

 /**
  * 进度条SeekBar
  */
 private SeekBar mSeekBar;

 /**
  * 显示进度的文字
  */
 private TextView mProgressTv;

 /**
  * 透明度
  */
 private int mAlpha;

 /**
  * 原始图片
  */
 private Bitmap mTempBitmap;

 /**
  * 模糊后的图片
  */
 private Bitmap mFinalBitmap;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  // 初始化视图
  initViews();

  // 获取图片
  mTempBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dayu);
  mFinalBitmap = BlurBitmap.blur(this, mTempBitmap);

  // 填充模糊后的图像和原图
  mBluredImage.setImageBitmap(mFinalBitmap);
  mOriginImg.setImageBitmap(mTempBitmap);

  // 处理seekbar滑动事件
  setSeekBar();
 }

 /**
  * 初始化视图
  */
 private void initViews() {
  mBluredImage = (ImageView) findViewById(R.id.activity_main_blured_img);
  mOriginImg = (ImageView) findViewById(R.id.activity_main_origin_img);
  mSeekBar = (SeekBar) findViewById(R.id.activity_main_seekbar);
  mProgressTv = (TextView) findViewById(R.id.activity_main_progress_tv);
 }

 /**
  * 处理seekbar滑动事件
  */
 private void setSeekBar() {
  mSeekBar.setMax(100);
  mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
   @Override
   public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    mAlpha = progress;
    mOriginImg.setAlpha((int) (255 - mAlpha * 2.55));
    mProgressTv.setText(String.valueOf(mAlpha));
   }

   @Override
   public void onStartTrackingTouch(SeekBar seekBar) {

   }

   @Override
   public void onStopTrackingTouch(SeekBar seekBar) {

   }
  });
 }
}

xml布局文件代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:id="@+id/activity_main"
 android:layout_width="match_parent"
 android:layout_height="match_parent">

 <FrameLayout
  android:layout_width="match_parent"
  android:layout_weight="1"
  android:layout_height="0dp">

  <ImageView
   android:id="@+id/activity_main_blured_img"
   android:scaleType="centerCrop"
   android:src="@drawable/dayu"
   android:layout_width="match_parent"
   android:layout_height="match_parent"/>

  <ImageView
   android:id="@+id/activity_main_origin_img"
   android:scaleType="centerCrop"
   android:layout_width="match_parent"
   android:layout_height="match_parent"/>
 </FrameLayout>

 <LinearLayout
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="80dp">

  <SeekBar
   android:layout_marginTop="@dimen/activity_vertical_margin"
   android:id="@+id/activity_main_seekbar"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_marginLeft="16dp"
   android:layout_marginRight="16dp"/>

  <TextView
   android:id="@+id/activity_main_progress_tv"
   android:text="0"
   android:textSize="24sp"
   android:layout_gravity="center"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
 </LinearLayout>

</LinearLayout>

效果如下:

怎么样?是不是很简单的样子?只需要调用模糊处理方法,并在SeekBar的滑动监听里面调用原图像的setAlpha()方法,来实现动态模糊效果。

你以为这样就完了?不不不,我们的目的并不是这么单纯,哦,不对,并不是这么简单。还记得文章开头的时候说了吗?我们的终极目的是要简单地模仿一下雅虎天气的界面效果。

仿雅虎天气界面

有了上面的基础,就可以很容易地模仿雅虎天气的界面效果。简单来说,在上面制作出的效果基础上,有以下两点需要注意的地方:

需要要监听滑动事件,然后再将背景图片调用setTop()方法,将图片向上平移一段距离。
要向上平移图片,还需要手动增加图片的高度,不然图片向上平移后,底部就会有留白。设置图片高度的核心代码如下:

WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point point = new Point();
display.getSize(point);
// 获取到ImageView的高度
int height = point.y;
ViewGroup.LayoutParams params = imageView.getLayoutParams();
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
// 将ImageView的高度增加100
params.height = height + 100;
// 应用更改设置
imageView.requestLayout();

完成上面两点的内容后,基本就可以模仿出雅虎天气的首页了。

结合第一个例子的demo,效果如下:

总结

以上就是本文的全部内容了,实现后的效果是不是很赞呢?感兴趣的朋友快快自己动手操作起来吧,希望本文对大家开发Android能有所帮助。

(0)

相关推荐

  • Android模糊处理实现图片毛玻璃效果

    本文实例讲解了Android 虚化图片.模糊图片.图片毛玻璃效果的实现方法,具体内容如下 效果如图: 在Android可以用RenderScript方便的实现这个方法: private void blur(Bitmap bkg, View view, float radius) { Bitmap overlay = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8

  • Android中实现布局背景模糊化处理的方法

    在模仿 IOS 密码输入页面的时候发现其背景有模糊处理,于是了解了一下并记录下来,以便使用.在Android 中具体实现方法如下 查考 http://www.jb51.net/article/64781.htm private void applyBlur() { // 获取壁纸管理器 WallpaperManager wallpaperManager = WallpaperManager.getInstance(this.getContext()); // 获取当前壁纸 Drawable wa

  • Android关于Glide的使用(高斯模糊、加载监听、圆角图片)

    高斯模糊.加载监听.圆角图片这些相信大家都很熟悉,那如何实现这些效果,请大家参考本文进行学习. 1.引用 compile 'com.github.bumptech.glide:glide:3.7.0' 2.加载图片 2.1 基本加载 Glide.with(context)     .load(url)     .into(imageView); 2.2 设置加载中和加载失败的情况 Glide.with(context) .load(url) .placeholder(R.drawable.loa

  • Android动态模糊效果的快速实现方法

    写在前面 现在,越来越多的App里面使用了模糊效果,这种模糊效果称之为高斯模糊.大家都知道,在Android平台上进行模糊渲染是一个相当耗CPU也相当耗时的操作,一旦处理不好,卡顿是在所难免的.一般来说,考虑到效率,渲染一张图片最好的方法是使用OpenGL,其次是使用C++/C,使用Java代码是效率是最低,速度也是最慢的.但是Android推出RenderScript之后,我们就有了选择,测试表明,使用RederScript的渲染效率和使用C++/C不相上下,但是使用RenderScript却

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

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

  • Android图片特效:黑白特效、圆角效果、高斯模糊

    1.黑白效果 复制代码 代码如下: /**     * 将彩色图转换为黑白图     *      * @param 位图     * @return 返回转换好的位图     */    public static Bitmap convertToBlackWhite(Bitmap bmp) {        int width = bmp.getWidth(); // 获取位图的宽        int height = bmp.getHeight(); // 获取位图的高 int[] pi

  • Android调用系统拍照裁剪图片模糊的解决方法

    在Android中,调用系统相机拍照时,将会接收到返回的图像数据,但是这些图片并不是全尺寸的图像,而是系统给的缩略图,当对拍照的图片进行裁切后显示时,得到的却是模糊的图片.下面针对这个问题提出解决的方法. 首先,我们知道调用系统的裁切是通过Intent intent = new Intent("com.android.camera.action.CROP"); 但是intent到底能够携带哪些数据呢,都有什么含义呢,我们可以看到如下: 上面包含了所有可选的操作,其中有一些非常重要的参数

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

    写在前面 最近一直在做毕设项目的准备工作,考虑到可能要用到一个模糊的效果,所以就学习了一些高斯模糊效果的实现.比较有名的就是 FastBlur 以及它衍生的一些优化方案,还有就是今天要说的RenderScript . 因为这东西是现在需要才去学习的,所以关于一些图像处理和渲染问题就不提了.不过在使用的过程中确实能感受到,虽然不同的方案都能实现相同的模糊效果,但是效率差别真的很大. 本篇文章实现的高斯模糊是根据下面这篇文章学习的,先推荐一下.本文内容与其内容差不多,只是稍微讲的详细一点,并修改了代

  • Android模糊处理简单实现毛玻璃效果

    自从iOS系统引入了Blur效果,也就是所谓的毛玻璃.模糊化效果.磨砂效果,各大系统就开始竞相模仿,这是怎样的一个效果呢,我们先来看一下,如下面的图片: 实现效果大家都知道了,如何在Android中实现呢,说白了就是对图片进行模糊化处理,小编先给大家讲一下Android高级模糊技术的原理,如下: 首先我创建了一个空的bitmap,把背景的一部分复制进去,之后我会对这个bitmap进行模糊处理并设置为TextView的背景. 通过这个bitmap保存Canvas的状态: 在父布局文件中把Canva

  • Android 实现图片模糊、高斯模糊、毛玻璃效果的三种方法

    在前几天写过一个使用glide-transformations的方法实现高斯模糊的方法,今天偶然间有发现一个大神写的另一个方法,感觉挺不错的,分享一下: 效果图: 原文链接:点击访问 这使用也很简单,导入依赖,使用模糊方法就行,就这两步搞定 依赖: compile 'net.qiujuer.genius:blur:2.0.0-beta4' 实现方法有三种,第一个是Java实现的,第二个和第三个是调用C语言实现的 ,具体的区别也就是代码执行的耗时操作时间,从图片中可以看出Java使用时间远大于c运

随机推荐