Android 编写高斯模糊功能

发布于:2025-08-29 ⋅ 阅读:(18) ⋅ 点赞:(0)

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;    // 更新间隔 ms

    private 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
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        // 注册 PreDraw 监听
        preDrawListener = () -> {
            long now = System.currentTimeMillis();
            if (now - lastUpdateTime >= updateInterval) {
                lastUpdateTime = now;
                blurAndInvalidate();
            }
            return true;
        };
        getViewTreeObserver().addOnPreDrawListener(preDrawListener);
    }

    @Override
    protected 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() {
        // 默认用父容器,或者用用户手动设置的 view
        View 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 缩放绘制到 bitmap
        bitmapCanvas.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();
    }

    @Override
    protected 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.blurview

import 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: Float
    private var downsampleFactor: Int
    private var updateInterval: Long

    private var bitmapBuffer: Bitmap? = null
    private var bitmapCanvas: Canvas? = null
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    // Android 12- 使用 RenderScript
    private var rs: android.renderscript.RenderScript? = null
    private var instBlur: android.renderscript.ScriptIntrinsicBlur? = null
    private var allocIn: android.renderscript.Allocation? = null
    private var allocOut: android.renderscript.Allocation? = null

    private var preDrawListener: ViewTreeObserver.OnPreDrawListener? = null
    private var lastUpdateTime = 0L

    // ✨ 可手动指定模糊源
    private var blurredView: View? = null

    init {
        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 = now
                blurAndInvalidate()
            }
            true
        }
        viewTreeObserver.addOnPreDrawListener(preDrawListener)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        preDrawListener?.let { viewTreeObserver.removeOnPreDrawListener(it) }

        bitmapBuffer?.recycle()
        bitmapBuffer = null

        rs?.destroy()
        rs = null
    }

    /** ✨ 外部调用,设置需要模糊的目标 View */
    fun setBlurredView(view: View?) {
        blurredView = view
    }

    /** 执行模糊并重绘自己 */
    private fun blurAndInvalidate() {
        val target = blurredView ?: parent as? View ?: return

        val width = target.width
        val height = target.height
        if (width == 0 || height == 0) return

        val bw = width / downsampleFactor
        val bh = height / downsampleFactor

        if (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+ 使用 RenderEffect
            setRenderEffect(RenderEffect.createBlurEffect(blurRadius, blurRadius, Shader.TileMode.CLAMP))
        } else {
            // Android 12- 使用 RenderScript
            bitmapBuffer?.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.BlurView
        android:id="@+id/blurView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- 遮罩层,调节背景亮度,仿iOS的高斯模糊 -->
    <View
        android:id="@+id/dimOverlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#66000000" />  <!-- 半透明黑色 -->

      <!-- 下面是你具体的UI代码 -->
</RelativeLayout>

以下是最后一点代码,具体怎么引用实现

// 配置 BlurView
        BlurView blurView = view.findViewById(R.id.blurView);
        if (getActivity() != null) {
            View decorView = getActivity().getWindow().getDecorView();
            blurView.setBlurredView(decorView); // 指定模糊背景为整个 Activity
        }

太久没写博客,有什么问题见谅
我写这一篇主打一个直接复制粘贴
注:java里面没有对Android12以上进行处理,如你的版本是12及以上,请参考kotiln的实现


网站公告

今日签到

点亮在社区的每一天
去签到