高斯模糊让你的UI更炫

Android截图及高斯模糊的实现

Posted by Anriku on March 6, 2018

今天我想介绍的是一个用于截屏并实现高斯模糊的一个工具类。提到高斯模糊,有很多苹果用户一定不会太陌生吧!在苹果设备上很多都实现了高斯模糊的效果。例如下面的这张图,背景就是桌面的高斯模糊图。那么在Android中如何实现这样的效果的!哈哈,马上进行揭晓。

image1

代码框架

BlurUtil

从上面我们可以看到整个高斯模糊的东西很少,其中的代码量也很少。但是,通过这个高斯模糊我们能学到的东西确蛮多。

下面介绍一下各个类或者接口的具体是干些什么的吧:

  • GaussianBlur:实现高斯模糊的核心类。
  • BitmapSource:原生(也就是没有经过高斯模糊处理的Bitmap)的Bitmap的来源的接口,所有获取原生Bitmap的类都要实现这个接口。
  • CaptureByDraw:一种通过View的draw()方法来获取原生Bitmap的方法。
  • CaptureByDrawingCache:一种通过getDrawingCache()方法来获取原生Bitmap的方法。
  • 没错代码就是这么出奇的简单,实现起来也特轻松。我们下面的学习把重点放在其它知识的扩展上。

代码具体分析

高斯模糊实现代码

/**
 * 此类是进行高斯模糊的核心类
 */
public class GaussianBlur {

    private BitmapSource bitmapSource;
    private RenderScript renderScript;
    private Bitmap bitmap;
    private boolean isBlur = false;

    private GaussianBlur() {
    }

    /**
     * 此类用于设置获取原生Bitmap的方式
     *
     * @param bitmapSource
     */
    public void setBitmapSource(BitmapSource bitmapSource) {
        this.bitmapSource = bitmapSource;
    }


    /**
     * 此类用于传入一个RenderScript对象
     *
     * @param renderScript
     */
    public void setRenderScript(RenderScript renderScript) {
        this.renderScript = renderScript;
    }


    /**
     * 此类用于获取android.R.id.content父类View截图的高斯模糊图
     *
     * @param radius
     * @return
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    public Bitmap getGaussianBlurBitmap(@IntRange(from = 1, to = 25) int radius) {
        if (!isBlur) {
            isBlur = true;
            //第一步,获取一个Bitmap(这里是一个UI的截图)
            bitmap = bitmapSource.getBitmap();
            //第二部,通过获取的Bitmap得到一个输入的Allocation和一个输出的Allocation
            Allocation input = Allocation.createFromBitmap(renderScript, bitmap);
            Allocation output = Allocation.createTyped(renderScript, input.getType());
            //ScriptIntrinsicBlur是一个实现高斯模糊的具体类
            ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript));
            //进行高斯模糊操作
            blur.setRadius(radius);
            blur.setInput(input);
            blur.forEach(output);
            output.copyTo(bitmap);
        }
        return bitmap;
    }


    /**
     * 进行单例,防止重复创建对象
     *
     * @return
     */
    public static GaussianBlur getInstance() {
        return GaussianBlurHolder.instance;
    }

    public static class GaussianBlurHolder {
        private static final GaussianBlur instance = new GaussianBlur();
    }
}

上面我们直接看到getGaussianBlurBitmap方法。此方法是用来让原生的Bitmap实现高斯模糊的类。

  • 第一步我们通过bitmapSource获取了一个Bitmap。这是一种策略模式,我们通过setBitmapSource()方法来设置了bitmapSource具体实现类。也就是这个类完全不会管bitmapSource是如何获取Bitmap的,你是截图来的,还是通过Drawable中的图片来的,我都不管,你只要给我一个Bitmap对象就对了。
  • 第二步,我们通过上面获取的Bitmap对象RenderScript环境得到一个输入的Allocation;然后,我们再通过输入的Allocation获得了一个输出的Allocation。Allocation不知道是什么对吧!其实Allocation就可以想成是种货币,一种就要在rs文件(这里是指ScriptIntrinsicBlur对应的rs文件,其实ScriptIntrinsicBlur是由rs文件反射出来的一个Java类)中使用的货币
  • 第三步,通过ScriptIntrinsicBlur类进行高斯模糊处理。注意setRaius值是1到25,为了不让输入的时候输到范围之外,在参数上已经通过@IntRange注解规定了其范围的。一旦超出范围就会报编译错误。

在介绍下面获取截屏的两个方法前,我们先来介绍一下Android的View的层级

下面是查看Android的View的层级的一个方法:View层级

通过Layout Inspector会出现下面的一个ViewTree,通过这个我们可以很明了的分析咱们视图的层级

ViewTree

View的层级通过上面的工具已经可以很明了了,我就不单独再啰嗦了。

其中我们截屏所要的部分就是android.R.id.content所指代的View也就是上面的content

获取截图

通过View的draw方法获取截图
/**
 * 此类为获取原生Bitmap的策略类
 */
public class CaptureByDraw implements BitmapSource {

    private Activity activity;

    public CaptureByDraw(Activity activity) {
        this.activity = activity;
    }


    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    @Override
    public Bitmap getBitmap() {
        View contentView = (View) activity.findViewById(android.R.id.content);
        Bitmap rawBitmap = Bitmap.createBitmap(contentView.getWidth(), contentView.getHeight(), Bitmap.Config.ARGB_4444);
        contentView.draw(new Canvas(rawBitmap));

        return rawBitmap;
    }
}
  • 第一步,我们通过activity.findViewById(android.R.id.content)获取了上面所说的content这个View(其实是一个ViewGroup但是ViewGroup其实还是一View就不纠结了)
  • 第二步,通过Bitmap的createBitmap方法获取一个Bitmap对象。
  • 第三步,通过View的draw方法将其视图放在一个Bitmap中。
通过View的getDrawingCache方法获取截图
/**
 * 此类是用于截图获得原生Bitmap的一个策略类
 */
public class CaptureByDrawingCache implements BitmapSource {

    private Activity activity;

    public CaptureByDrawingCache(Activity activity) {
        this.activity = activity;
    }

    @Override
    public Bitmap getBitmap() {
        View contentView = activity.findViewById(android.R.id.content);
        contentView.setDrawingCacheEnabled(true);
        return contentView.getDrawingCache();
    }
}

emmm…这个代码就更少了!我们来看一下。

  • 第一步,同样获取content这个View。
  • 第二步,通过View的setDrawingCacheEnabled(true)的方法启用Cache。
  • 第三步,通过View的getDrawingCache来获取View缓存的Bitmap。

总结

  • 其实实现背景高斯模糊很简单。具体就是两步。
    • 第一步,获取要模糊的View,然后获取这个View的截图。
    • 第二步,进行高斯模糊。
  • 然后就是高斯模糊会用到RenderScript这个我会在后面进行一下介绍。谢谢大家花时间看我的博客!^ ^

博客参考

完整代码链接

GaussianBlurDemo