亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android實(shí)現(xiàn)動(dòng)態(tài)高斯模糊背景效果

 更新時(shí)間:2025年04月20日 09:54:42   作者:Katie。  
在現(xiàn)代 Android UI 中,動(dòng)態(tài)高斯模糊背景 常見(jiàn)于對(duì)話(huà)框或彈窗后面的模糊遮罩,相比靜態(tài)模糊圖,動(dòng)態(tài)模糊可隨著內(nèi)容滾動(dòng)或變化實(shí)時(shí)更新,使界面更具層次感與沉浸感,所以本文給大家介紹了Android實(shí)現(xiàn)動(dòng)態(tài)高斯模糊背景效果,需要的朋友可以參考下

一、項(xiàng)目介紹

在現(xiàn)代 Android UI 中,動(dòng)態(tài)高斯模糊背景 常見(jiàn)于:

  • 對(duì)話(huà)框或彈窗后面的模糊遮罩

  • 側(cè)滑菜單后面的實(shí)時(shí)模糊

  • 滾動(dòng)內(nèi)容時(shí)的背景模糊

  • 視頻/圖像播放器的模糊化背景

相比靜態(tài)模糊圖,動(dòng)態(tài)模糊可隨著內(nèi)容滾動(dòng)或變化實(shí)時(shí)更新,使界面更具層次感與沉浸感。但實(shí)時(shí)高斯模糊也帶來(lái)性能挑戰(zhàn),需要在兼顧流暢度與畫(huà)面清晰度之間權(quán)衡。

本項(xiàng)目目標(biāo)是:

  1. 提供一個(gè)通用的 BlurView 自定義控件,能在任意 API 級(jí)別上動(dòng)態(tài)模糊其后面的視圖。

  2. 在 API 31+ 上使用 RenderEffect(硬件加速、性能佳),在 API 21–30 上使用 RenderScript(軟件/兼容)。

  3. 支持可調(diào)節(jié)的 模糊半徑、降采樣比例(downsample)與 更新頻率

  4. 演示如何在布局中快速集成:在布局文件或者代碼中一行即可使用。

  5. 兼顧 生命周期,避免泄漏和無(wú)效更新。

二、相關(guān)技術(shù)與知識(shí)

  1. RenderEffect(API 31+)

    • android.graphics.RenderEffect.createBlurEffect(radiusX, radiusY, Shader.TileMode.CLAMP)

    • 直接通過(guò) View.setRenderEffect() 給控件或背景添加實(shí)時(shí)高斯模糊。

  2. RenderScript 與 ScriptIntrinsicBlur(API 17+)

    • 使用支持模式 renderscriptSupportModeEnabled,在 build.gradle 中啟用:

android {
  defaultConfig {
    renderscriptTargetApi 21
    renderscriptSupportModeEnabled true
  }
}
  • ScriptIntrinsicBlur 接受輸入 Allocation,輸出模糊后的 Allocation,再拷貝回 Bitmap。
  1. 降采樣(Downsampling)

    • 先將目標(biāo) Bitmap 縮小若干倍(如 1/4),再模糊,可大幅提升性能;

    • 最終將模糊圖拉伸回原始大小顯示,肉眼看差別不大。

  2. ViewTreeObserver.OnPreDrawListener

    • 在每次 BlurView 自身重繪前捕獲底層內(nèi)容快照,生成模糊圖并應(yīng)用。

    • 需在 onAttachedToWindow() 注冊(cè),在 onDetachedFromWindow() 注銷(xiāo)。

  3. SurfaceView / TextureView / GLSurfaceView

    • 這些 View 的內(nèi)容無(wú)法通過(guò)常規(guī)方式取到 Bitmap;需特殊處理或跳過(guò)。

  4. 性能權(quán)衡

    • 模糊半徑越大、采樣縮放越小,效果越柔和,但計(jì)算量增加;

    • 需要設(shè)置合理的 更新間隔,避免每幀都重模糊。

三、實(shí)現(xiàn)思路

  1. 自定義控件 BlurView

    • 繼承 FrameLayout,讓所有子 View 顯示在模糊圖之上;

    • 在背景層繪制模糊后的快照;

    • 通過(guò)自定義屬性支持 blurRadiusdownsampleFactor、updateInterval

  2. 布局集成

    • 在 activity_main.xml 或其他布局中,將內(nèi)容放在 BlurView 之后,或?qū)?nbsp;BlurView 放在內(nèi)容之上并設(shè)置 match_parent,即可遮罩。

  3. BlurView 內(nèi)部邏輯

    • 在 onAttachedToWindow()

      • 判斷 API 級(jí)別,初始化相應(yīng)模糊引擎(RenderEffect 或 RenderScript);

      • 注冊(cè) ViewTreeObserver.OnPreDrawListener;

    • 在 OnPreDrawListener

      • 每隔 updateInterval ms 獲取父容器或指定目標(biāo) View 的快照(getDrawingCache() 或 Bitmap.createBitmap(view));

      • 根據(jù) API 級(jí)別執(zhí)行模糊:RenderEffect 直接調(diào)用 setRenderEffect();Renderscript 生成 Bitmap;

      • 將模糊結(jié)果繪制到 Canvas

  4. 釋放資源

    • 在 onDetachedFromWindow()

      • 注銷(xiāo) OnPreDrawListener;

      • 銷(xiāo)毀 RenderScript rs.destroy();

四、完整代碼

// ==============================================
// 文件:BlurDemoActivity.java
// 功能:演示動(dòng)態(tài)高斯模糊背景的使用
// 包含:布局 XML,Gradle 配置,BlurView 控件源碼
// ==============================================
 
package com.example.blurviewdemo;
 
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
 
/*
=========================== app/build.gradle ===========================
android {
  compileSdk 33
  defaultConfig {
    applicationId "com.example.blurviewdemo"
    minSdk 21
    targetSdk 33
    // 啟用 RenderScript 兼容模式
    renderscriptTargetApi 21
    renderscriptSupportModeEnabled true
  }
  // ...
}
dependencies {
  implementation 'androidx.appcompat:appcompat:1.5.1'
  implementation 'com.google.code.gson:gson:2.9.0' // 如需 JSON 解析
}
=========================== Gradle 結(jié)束 ===========================
*/
 
/*
=========================== res/layout/activity_main.xml ===========================
<?xml version="1.0" encoding="utf-8"?>
<!-- 父布局:背景內(nèi)容 -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/rootContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- 1. 背景內(nèi)容示例 -->
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/your_large_image"/>
    <!-- 2. 動(dòng)態(tài)模糊遮罩層 -->
    <com.example.blurviewdemo.BlurView
        android:id="@+id/blurView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:blurRadius="16"
        app:downsampleFactor="4"
        app:updateInterval="100"/>
    <!-- 3. 前端 UI 元素 -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="動(dòng)態(tài)高斯模糊示例"
        android:textSize="24sp"
        android:textColor="#FFFFFFFF"
        android:layout_gravity="center"/>
</FrameLayout>
=========================== 布局結(jié)束 ===========================
*/
 
public class BlurDemoActivity extends AppCompatActivity {
    @Override protected void onCreate(@Nullable Bundle s) {
        super.onCreate(s);
        setContentView(R.layout.activity_main);
        // 無(wú)需額外代碼,BlurView 會(huì)在 attached 時(shí)自動(dòng)工作
    }
}
 
// ==============================================
// 文件:BlurView.java
// 功能:通用的動(dòng)態(tài)高斯模糊遮罩控件
// ==============================================
 
package com.example.blurviewdemo;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.os.Build;
import android.renderscript.*;
import android.util.AttributeSet;
import android.view.*;
import android.widget.FrameLayout;
 
import androidx.annotation.Nullable;
 
public class BlurView extends FrameLayout {
 
    private int blurRadius;         // 模糊半徑
    private int downsampleFactor;   // 降采樣倍數(shù)
    private long updateInterval;    // 更新間隔 ms
 
    private Bitmap bitmapBuffer;
    private Canvas  bitmapCanvas;
    private Paint   paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private boolean useRenderEffect;
    private RenderScript rs;
    private ScriptIntrinsicBlur instBlur;
    private Allocation allocIn, allocOut;
 
    private ViewTreeObserver.OnPreDrawListener preDrawListener;
    private long lastUpdateTime = 0;
 
    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, 10);
        downsampleFactor = a.getInt(R.styleable.BlurView_downsampleFactor, 4);
        updateInterval   = a.getInt(R.styleable.BlurView_updateInterval, 100);
        a.recycle();
 
        // 決定使用哪種模糊方式
        useRenderEffect = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
 
        if (!useRenderEffect) {
            // 初始化 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();
        // 注冊(cè) PreDraw 監(jiān)聽(tīng)
        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 (rs != null) rs.destroy();
    }
 
    /** 執(zhí)行模糊并重繪自己 */
    private void blurAndInvalidate() {
        // 獲取父容器快照
        View root = (View) getParent();
        if (root == null) return;
 
        int width  = root.getWidth();
        int height = root.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);
        }
        // 將 root 縮放繪制到 bitmap
        bitmapCanvas.save();
        bitmapCanvas.scale(1f/downsampleFactor, 1f/downsampleFactor);
        root.draw(bitmapCanvas);
        bitmapCanvas.restore();
 
        // 模糊
        if (useRenderEffect) {
            // API 31+: 直接在自己上設(shè)置 RenderEffect
            RenderEffect effect = RenderEffect.createBlurEffect(
                blurRadius, blurRadius, Shader.TileMode.CLAMP);
            setRenderEffect(effect);
        } else {
            // RenderScript 模糊
            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);
            // 將模糊結(jié)果拷貝到自己的 bitmap
            invalidate();
        }
    }
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!useRenderEffect && bitmapBuffer!=null) {
            // 繪制放大回屏幕
            canvas.save();
            canvas.scale(downsampleFactor, downsampleFactor);
            canvas.drawBitmap(bitmapBuffer, 0, 0, paint);
            canvas.restore();
        }
    }
}
 
// ==============================================
// res/values/attrs.xml(整合在此)
// ==============================================
/*
<resources>
  <declare-styleable name="BlurView">
    <attr name="blurRadius" format="integer"/>
    <attr name="downsampleFactor" format="integer"/>
    <attr name="updateInterval" format="integer"/>
  </declare-styleable>
</resources>
*/
 

五、方法解讀

  1. 屬性讀取

    • blurRadius:高斯模糊半徑(最大 25);

    • downsampleFactor:降采樣比例,越大性能越好但細(xì)節(jié)越差;

    • updateInterval:兩次模糊之間的最小間隔(避免每幀都模糊)。

  2. 模糊引擎選擇

    • API 31+ 調(diào)用 View.setRenderEffect(),由系統(tǒng)硬件加速處理;

    • API 21–30 使用 RenderScript 的 ScriptIntrinsicBlur,在軟件層或兼容層執(zhí)行。

  3. 預(yù)繪制監(jiān)聽(tīng)

    • 在 OnPreDrawListener 中獲取父 View 快照,并進(jìn)行降采樣 & 模糊;

    • 每次更新后調(diào)用 invalidate(),觸發(fā) onDraw()。

  4. 降采樣再放大

    • 先對(duì)父 View 按 1/downsampleFactor 比例繪制到小 Bitmap,再模糊,最后在 onDraw() 中放大回去;

    • 大幅降低模糊計(jì)算量,保證流暢。

  5. 生命周期管理

    • 在 onAttachedToWindow() 注冊(cè)監(jiān)聽(tīng),onDetachedFromWindow() 注銷(xiāo)并銷(xiāo)毀 RenderScript。

    • 確保在 View 不可見(jiàn)或被銷(xiāo)毀時(shí)不再占用資源。

六、項(xiàng)目總結(jié)

  • 性能與兼容

    • 推薦 API 31+ 使用 RenderEffect,無(wú)需創(chuàng)建中間 Bitmap,性能最佳;

    • API 21–30 使用 RenderScript + 降采樣,可在大多數(shù)設(shè)備保持 30fps 左右;

    • 合理調(diào)整 downsampleFactor(建議 48)與 updateInterval(建議 100200ms)。

  • 使用場(chǎng)景

    • 對(duì)話(huà)框后模糊(僅首次靜態(tài)一次),可直接在布局中包裹對(duì)話(huà)框根視圖;

    • 滾動(dòng)時(shí)背景模糊(例如 RecyclerView 下面),可將 BlurView 放在內(nèi)容之上;

    • 視頻或動(dòng)畫(huà)背景模糊,需保證 updateInterval 足夠長(zhǎng)以免過(guò)度消耗。

  • 擴(kuò)展

    1. 邊緣遮罩:在模糊后繪制漸變遮罩邊緣;

    2. 抖動(dòng)補(bǔ)償:在快速滾動(dòng)時(shí)暫停模糊更新,滾動(dòng)停止后再模糊;

    3. 多區(qū)域模糊:支持對(duì)某個(gè)子區(qū)域進(jìn)行模糊,而不是全屏;

    4. Jetpack Compose:Compose 1.3+ 中使用 Modifier.graphicsLayer { renderEffect = … } 簡(jiǎn)單實(shí)現(xiàn);

以上就是Android實(shí)現(xiàn)動(dòng)態(tài)高斯模糊背景效果的詳細(xì)內(nèi)容,更多關(guān)于A(yíng)ndroid高斯模糊背景的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論