Android 编写高斯模糊功能
1. 这篇文章主要是写的Androidsupper库的高斯模糊,主打一个直接复制粘贴可以直接使用,如需要Android12以后,需要自行百度进行适配
2. 首先是用到的一些自定义属性,在你的xml下,value文件夹attr文件里面添加以下自定义属性
一、
<declare-styleable name="BlurView"><attr name="blurRadius" format="integer" /><attr name="downsampleFactor" format="integer" /><attr name="updateInterval" format="integer" /></declare-styleable>
1. blurRadius —— 模糊半径
定义:控制高斯模糊的模糊程度,数值越大,模糊越“散”,看起来更模糊。
表现:
小值(比如 5):模糊轻微,背景还能依稀看到。
大值(比如 20):模糊强烈,背景几乎看不清。
影响性能:半径越大,模糊计算量越多,性能开销更大。
2. downsampleFactor —— 降采样倍数
定义:在做模糊之前,先把画面缩小(降采样),再做模糊,最后再放大到原来的大小。
作用:
降低需要计算的像素数量,提高模糊运算速度。
同时也会让模糊看起来更“糊”,因为缩小再放大损失了细节。
举例:
downsampleFactor = 1 → 不缩小,清晰但耗性能。
downsampleFactor = 4 → 缩小到 1/4 再模糊,性能大幅提升,但细节损失明显。
3、 updateInterval 这个是更新间隔,根据需求自行调整
二、BlurView的详细代码,我这里贴一份java的和一份kotlin的,kotiln的事最新的版本直接进行引用
public class BlurView extends FrameLayout {private int blurRadius; // 模糊半径private int downsampleFactor; // 降采样倍数private long updateInterval; // 更新间隔 msprivate Bitmap bitmapBuffer;private Canvas bitmapCanvas;private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);private RenderScript rs;private ScriptIntrinsicBlur instBlur;private Allocation allocIn, allocOut;private ViewTreeObserver.OnPreDrawListener preDrawListener;private long lastUpdateTime = 0;// ✨ 新增:可以手动指定模糊源private View blurredView;public BlurView(Context c) {this(c, null);}public BlurView(Context c, AttributeSet attrs) {this(c, attrs, 0);}public BlurView(Context c, AttributeSet attrs, int defStyle) {super(c, attrs, defStyle);// 读取属性TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.BlurView);blurRadius = a.getInt(R.styleable.BlurView_blurRadius, 15);downsampleFactor = a.getInt(R.styleable.BlurView_downsampleFactor, 4);updateInterval = a.getInt(R.styleable.BlurView_updateInterval, 100);a.recycle();// 初始化 RenderScript 模糊rs = RenderScript.create(c);instBlur = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));instBlur.setRadius(blurRadius);setWillNotDraw(false); // 允许 onDraw}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();// 注册 PreDraw 监听preDrawListener = () -> {long now = System.currentTimeMillis();if (now - lastUpdateTime >= updateInterval) {lastUpdateTime = now;blurAndInvalidate();}return true;};getViewTreeObserver().addOnPreDrawListener(preDrawListener);}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();// 清理getViewTreeObserver().removeOnPreDrawListener(preDrawListener);if (bitmapBuffer != null) {bitmapBuffer.recycle();bitmapBuffer = null;}if (rs != null) rs.destroy();}/*** ✨ 新增:外部调用,设置需要模糊的目标 View*/public void setBlurredView(View view) {this.blurredView = view;}/*** 执行模糊并重绘自己*/private void blurAndInvalidate() {// 默认用父容器,或者用用户手动设置的 viewView target = blurredView != null ? blurredView : (View) getParent();if (target == null) return;int width = target.getWidth();int height = target.getHeight();if (width == 0 || height == 0) return;int bw = width / downsampleFactor;int bh = height / downsampleFactor;// 初始化缓存if (bitmapBuffer == null ||bitmapBuffer.getWidth() != bw ||bitmapBuffer.getHeight() != bh) {bitmapBuffer = Bitmap.createBitmap(bw, bh, Bitmap.Config.ARGB_8888);bitmapCanvas = new Canvas(bitmapBuffer);}// 将 target 缩放绘制到 bitmapbitmapCanvas.save();bitmapCanvas.scale(1f / downsampleFactor, 1f / downsampleFactor);target.draw(bitmapCanvas);bitmapCanvas.restore();if (allocIn != null) allocIn.destroy();if (allocOut != null) allocOut.destroy();allocIn = Allocation.createFromBitmap(rs, bitmapBuffer);allocOut = Allocation.createTyped(rs, allocIn.getType());instBlur.setInput(allocIn);instBlur.forEach(allocOut);allocOut.copyTo(bitmapBuffer);// 触发重绘invalidate();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (bitmapBuffer != null) {// 绘制放大回屏幕canvas.save();canvas.scale(downsampleFactor, downsampleFactor);canvas.drawBitmap(bitmapBuffer, 0, 0, paint);canvas.restore();}}
}
以下是kotiln的代码
package com.example.blurviewimport android.content.Context
import android.graphics.*
import android.os.Build
import android.util.AttributeSet
import android.view.*
import android.widget.FrameLayout
import androidx.annotation.RequiresApi
import androidx.core.view.ViewCompat@Suppress("DEPRECATION")
class BlurView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyle: Int = 0
) : FrameLayout(context, attrs, defStyle) {private var blurRadius: Floatprivate var downsampleFactor: Intprivate var updateInterval: Longprivate var bitmapBuffer: Bitmap? = nullprivate var bitmapCanvas: Canvas? = nullprivate val paint = Paint(Paint.ANTI_ALIAS_FLAG)// Android 12- 使用 RenderScriptprivate var rs: android.renderscript.RenderScript? = nullprivate var instBlur: android.renderscript.ScriptIntrinsicBlur? = nullprivate var allocIn: android.renderscript.Allocation? = nullprivate var allocOut: android.renderscript.Allocation? = nullprivate var preDrawListener: ViewTreeObserver.OnPreDrawListener? = nullprivate var lastUpdateTime = 0L// ✨ 可手动指定模糊源private var blurredView: View? = nullinit {val a = context.obtainStyledAttributes(attrs, R.styleable.BlurView)blurRadius = a.getInt(R.styleable.BlurView_blurRadius, 15).toFloat()downsampleFactor = a.getInt(R.styleable.BlurView_downsampleFactor, 4)updateInterval = a.getInt(R.styleable.BlurView_updateInterval, 100).toLong()a.recycle()// 初始化 RenderScript (仅 Android 12 以下)if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {rs = android.renderscript.RenderScript.create(context)instBlur = android.renderscript.ScriptIntrinsicBlur.create(rs,android.renderscript.Element.U8_4(rs)).apply {radius = blurRadius}}setWillNotDraw(false)}override fun onAttachedToWindow() {super.onAttachedToWindow()preDrawListener = ViewTreeObserver.OnPreDrawListener {val now = System.currentTimeMillis()if (now - lastUpdateTime >= updateInterval) {lastUpdateTime = nowblurAndInvalidate()}true}viewTreeObserver.addOnPreDrawListener(preDrawListener)}override fun onDetachedFromWindow() {super.onDetachedFromWindow()preDrawListener?.let { viewTreeObserver.removeOnPreDrawListener(it) }bitmapBuffer?.recycle()bitmapBuffer = nullrs?.destroy()rs = null}/** ✨ 外部调用,设置需要模糊的目标 View */fun setBlurredView(view: View?) {blurredView = view}/** 执行模糊并重绘自己 */private fun blurAndInvalidate() {val target = blurredView ?: parent as? View ?: returnval width = target.widthval height = target.heightif (width == 0 || height == 0) returnval bw = width / downsampleFactorval bh = height / downsampleFactorif (bitmapBuffer == null || bitmapBuffer?.width != bw || bitmapBuffer?.height != bh) {bitmapBuffer = Bitmap.createBitmap(bw, bh, Bitmap.Config.ARGB_8888)bitmapCanvas = Canvas(bitmapBuffer!!)}bitmapCanvas?.apply {save()scale(1f / downsampleFactor, 1f / downsampleFactor)target.draw(this)restore()}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {// Android 12+ 使用 RenderEffectsetRenderEffect(RenderEffect.createBlurEffect(blurRadius, blurRadius, Shader.TileMode.CLAMP))} else {// Android 12- 使用 RenderScriptbitmapBuffer?.let { bmp ->allocIn?.destroy()allocOut?.destroy()allocIn = android.renderscript.Allocation.createFromBitmap(rs, bmp)allocOut = android.renderscript.Allocation.createTyped(rs, allocIn!!.type)instBlur?.setInput(allocIn)instBlur?.forEach(allocOut)allocOut?.copyTo(bmp)}}invalidate()}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)bitmapBuffer?.let { bmp ->if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {canvas.save()canvas.scale(downsampleFactor.toFloat(), downsampleFactor.toFloat())canvas.drawBitmap(bmp, 0f, 0f, paint)canvas.restore()}}}
}
还有需要再build文件内需要配置属性
在android下defaultConfig内添加以下两个属性:
renderscriptTargetApi 21
renderscriptSupportModeEnabled true
三、如何使用
上面已经写了相关的文件和配置,下面是如何在xml文件中如何使用,以及一些注意事项:
先是xml怎么用:
<?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:background="#00000000"><!-- 2. 动态模糊遮罩层 --><com.cars.mobile.view.BlurViewandroid:id="@+id/blurView"android:layout_width="match_parent"android:layout_height="match_parent" /><!-- 遮罩层,调节背景亮度,仿iOS的高斯模糊 --><Viewandroid:id="@+id/dimOverlay"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#66000000" /> <!-- 半透明黑色 --><!-- 下面是你具体的UI代码 -->
</RelativeLayout>
以下是最后一点代码,具体怎么引用实现
// 配置 BlurViewBlurView blurView = view.findViewById(R.id.blurView);if (getActivity() != null) {View decorView = getActivity().getWindow().getDecorView();blurView.setBlurredView(decorView); // 指定模糊背景为整个 Activity}
太久没写博客,有什么问题见谅
我写这一篇主打一个直接复制粘贴
注:java里面没有对Android12以上进行处理,如你的版本是12及以上,请参考kotiln的实现