【安卓13-Framework】SystemUI定制之屏蔽下拉状态栏部分快捷按钮

1、需求

屏蔽下拉状态栏谷歌录屏、省电模式、二维码扫描器等快捷按钮。

2、修改路径

普及:安卓的SystemUI包提供了状态栏、导航栏、通知中心等重要的用户界面元素。
状态栏小部件UI显示修改路径:frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java

3、修改思路

下拉状态栏属于系统UI的一部分,位于SystemUI包下,不同安卓版本这个包的代码有些许出入,但是万变不离其宗,掌握修改原理即可,下面来一步步分析如何修改以及为什么要这样修改:

(1)修改方法

打开这个文件:frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java,定位到这个方法:
在这里插入图片描述
这个方法做了两件事,第一件事是加载“存货”(Stock tiles),什么是存货呢,就是安卓原生自带的小部件,比如WiFi、蓝牙、定位等快捷按钮,第二件事是加载工程师自定义的小部件(Custom tiles),在这里我们先不研究自定义的小部件,以后有时间再研究。
既然是加载“存货”,那这些存货从哪里来呢,先不看这个,我先把修改代码放出来,若不想研究原理可不看后面,修改代码如下:

    @Nullable
    protected QSTileImpl createTileInternal(String tileSpec) {
        // Stock tiles.
        if (mTileMap.containsKey(tileSpec)
                // We should not return a Garbage Monitory Tile if the build is not Debuggable
                && (!tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC) || Build.IS_DEBUGGABLE)) {
            //屏蔽下拉状态栏中的几个tile,返回空对象
            Set<String> multipleStrings = new HashSet<>();
            multipleStrings.add("screenrecord"); //谷歌录屏
            multipleStrings.add("battery");  // 省电模式
            multipleStrings.add("qr_code_scanner"); // 二维码扫描器
            if (multipleStrings.contains(tileSpec)) {
                return null;
            }
            return mTileMap.get(tileSpec).get();
        }

        // Custom tiles
        if (tileSpec.startsWith(CustomTile.PREFIX)) {
            return CustomTile.create(
                    mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
        }

        // Broken tiles.
        Log.w(TAG, "No stock tile spec: " + tileSpec);
        return null;
    }

好了,结束!

nonono,开个玩笑,接下来讲解原理,因为有些版本不是这样改的,待会就知道了。

(2)修改原理

首先我们知道,谷歌的代码写的都是很优雅的,想完全看懂不容易,这里我们只啃一部分,那就是下拉状态栏那些快捷按钮(QS tile)是如何添加的。
首先我们观察SystemUI目录,里面有很多子目录,大部分都是每一个模块细化出来,比如WiFi相关UI放在一个目录,声音UI放一个目录,目录里面基本都是写小模块的具体实现。

而qs目录就是专门负责实现通知栏或状态栏这个面板的各种功能和交互效果,比如图标的显示、动画效果、点击事件的处理等。
这里我们主要看qs目录,该目录结构如图:
在这里插入图片描述
其中,QSContainerImpl.java 这个类作为下拉状态栏快速面板的布局承载各种View,QSTileHost.java类是个接口实现类,里面实现了对tile的增删改查操作:
在这里插入图片描述
例如,我们想移除谷歌相机,同样可以在布局初始化处调用removeTile方法:

    @Override
    public void removeTile(String spec) {
        if (spec.startsWith(CustomTile.PREFIX)) {
            // If the tile is removed (due to it not actually existing), mark it as removed. That
            // way it will be marked as newly added if it appears in the future.
            setTileAdded(CustomTile.getComponentFromSpec(spec), mCurrentUser, false);
        }
        mMainExecutor.execute(() -> changeTileSpecs(tileSpecs-> tileSpecs.remove(spec)));
    }

传入一个字符串“screenrecord”,关于怎么知道各个组件的键值,后面会讲。
注意,调用这个方法只能移除原生自带的一些组件键值,addTile(String spec) 这个方法也是只能创建自带的组件。
接下来回到上面修改的实现类QSFactoryImpl.java里面看它是如何移除谷歌相机的,首先,这个mTileMap是一个map 集合,里面加载了所有的谷歌原生tile键值
在这里插入图片描述
我们看下这个map 是从哪里传递过来的,在这里插入图片描述
看到是通过构造方法传递,继续找哪里调用这个构造方法
在这里插入图片描述
在这个类里面,我们看到tileMap在这里传递了进来,看一下tileMap的结构

        val tileMap = mutableMapOf<String, Provider<QSTileImpl<*>>>(
            "internet" to Provider { internetTile },
            "bt" to Provider { bluetoothTile },
            "dnd" to Provider { dndTile },
            "inversion" to Provider { colorInversionTile },
            "airplane" to Provider { airplaneTile },
            "work" to Provider { workTile },
            "rotation" to Provider { rotationTile },
            "flashlight" to Provider { flashlightTile },
            "location" to Provider { locationTile },
            "cast" to Provider { castTile },
            "hotspot" to Provider { hotspotTile },
            "battery" to Provider { batterySaverTile },
            "saver" to Provider { dataSaverTile },
            "night" to Provider { nightDisplayTile },
            "nfc" to Provider { nfcTile },
            "dark" to Provider { darkModeTile },
            "screenrecord" to Provider { screenRecordTile },
            "reduce_brightness" to Provider { reduceBrightColorsTile },
            "cameratoggle" to Provider { cameraToggleTile },
            "mictoggle" to Provider { microphoneToggleTile },
            "controls" to Provider { deviceControlsTile },
            "alarm" to Provider { alarmTile },
            "wallet" to Provider { quickAccessWalletTile },
            "qr_code_scanner" to Provider { qrCodeScannerTile },
            "onehanded" to Provider { oneHandedModeTile },
            "color_correction" to Provider { colorCorrectionTile },
            "dream" to Provider { dreamTile },
            "font_scaling" to Provider { fontScalingTile }
        )

这段代码是 Kotlin 语言编写的,它定义了一个名为 tileMap 的可变映射(MutableMap)。这个映射的键是字符串类型(String),而值是 Provider<QSTileImpl<*>> 类型。Provider 是 Dagger 框架中的一个接口,它代表了一个对象的延迟提供者,即当需要时才会创建该对象。以**“screenrecord” to Provider { screenRecordTile },**为例,这个screenrecord键值映射了一个provider,当请求screenrecord键时,会返回一个screenRecordTile对象,这个对象就是谷歌录屏这个按钮的实体,实现了谷歌录屏这个快捷按钮的一些行为,比如点击录制,停止录制等。
我们回到创建过程看
在这里插入图片描述
在创建过程时加入过滤集合,将不需要显示的快捷按钮的键值加到set中,如果集合包含要创建的tile,就返回一个空对象,上面对对象判空,如果为空,则不会创建tile。

明白了创建原理,改起来就很简单了,比如在安卓其他版本,这个实现类并不是这样写的
在这里插入图片描述
这里这个实现类是直接在当前类根据tile标识返回对应的实体

    @Nullable
    protected QSTileImpl createTileInternal(String tileSpec) {
        // Stock tiles.
        switch (tileSpec) {
            case "wifi":
                return mWifiTileProvider.get();
            case "internet":
                return mInternetTileProvider.get();
            case "bt":
                return mBluetoothTileProvider.get();
            /*case "cell":
                return mCellularTileProvider.get();*/
            case "dnd":
                return mDndTileProvider.get();
            case "inversion":
                return mColorInversionTileProvider.get();
            case "airplane":
                return mAirplaneModeTileProvider.get();
            /*case "work":
                return mWorkModeTileProvider.get();
            case "rotation":
                return mRotationLockTileProvider.get();
            case "flashlight":
                return mFlashlightTileProvider.get();
            case "location":
                return mLocationTileProvider.get();*/
            case "cast":
                return mCastTileProvider.get();
            case "hotspot":
                return mHotspotTileProvider.get();
            /*case "battery":
                return mBatterySaverTileProvider.get();
            case "saver":
                return mDataSaverTileProvider.get();
            case "night":
                return mNightDisplayTileProvider.get();
            case "nfc":
                return mNfcTileProvider.get();
            case "dark":
                return mUiModeNightTileProvider.get();*/
            /*case "screenrecord":
                return mScreenRecordTileProvider.get();*/
            /*case "reduce_brightness":
                return mReduceBrightColorsTileProvider.get();
            case "cameratoggle":
                return mCameraToggleTileProvider.get();
            case "mictoggle":
                return mMicrophoneToggleTileProvider.get();*/
            case "controls":
                return mDeviceControlsTileProvider.get();
            /*case "alarm":
                return mAlarmTileProvider.get();
            case "wallet":
                return mQuickAccessWalletTileProvider.get();
            case "qr_code_scanner":
                return mQRCodeScannerTileProvider.get();
            case "onehanded":
                return mOneHandedModeTileProvider.get();*/
            case "color_correction":
                return mColorCorrectionTileProvider.get();
            /*case "dream":
                return mDreamTileProvider.get();*/
        }

        // Custom tiles
        if (tileSpec.startsWith(CustomTile.PREFIX)) {
            return CustomTile.create(
                    mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
        }

        // Debug tiles.
        /*if (Build.IS_DEBUGGABLE) {
            if (tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC)) {
                return mMemoryTileProvider.get();
            }
        }*/

        // Broken tiles.
        Log.w(TAG, "No stock tile spec: " + tileSpec);
        return null;
    }

这种也很简单,直接注释掉不要的tile即可,这个版本的实现类写的稍微就没那么优雅了,但是基本原理都一样。
好了,先写到这里。

3、总结

系统下拉状态栏包含的内容还是很多的,目前只分析了如何移除快捷按钮,继续刨析还可以实现增加自定义的按钮,不过得遵循谷歌的一套做法,再深入了解还可以美化状态栏,这些就留给以后去慢慢分析了。

共勉:分享知识,共同进步!

<think>嗯,用户想在FrameworkSystemUI中实现下拉背景的实时模糊效果。首先,我需要回忆一下Android系统中关于模糊处理的相关知识。记得在Android 12之后,Google引入了Blur效果相关的API,可能是在RenderEffect或者WindowManager里。不过用户提到的可能是在更底层的实现,比如修改SystemUI的代码。 用户的问题是关于实时模糊,所以需要动态处理下拉背景的视图。可能需要用到RenderNode和Canvas的硬件加速功能。记得以前处理模糊效果时,可以通过设置RenderEffect的BlurEffect来实现,但需要考虑性能问题,因为实时模糊比较耗资源。 接下来,我需要查看用户提供的引用内容。引用[1]提到在SystemUI下拉状态栏中去掉QSTileView的着色效果,修改了QSIconViewImpl.java和QSTileBaseView.java。引用[2]是关于状态栏透明背景的实现,涉及ActivityThread.java和StatusBar.java。这说明用户可能希望在下拉背景的基础上,应用模糊效果,可能需要结合透明背景的处理。 实现实时模糊的核心步骤可能包括:1. 获取下拉背景的视图层级,找到需要模糊的根视图。2. 在绘制时应用模糊效果。可能需要重写某个View的draw方法,或者在布局加载时添加模糊处理。另外,考虑性能优化,比如使用硬件加速、降低采样率或设置模糊半径的动态调整。 需要确定在SystemUI中的哪个类负责下拉背景的绘制。可能是在NotificationPanelView.java或者类似的文件中。然后,在onDraw或者dispatchDraw方法中插入模糊处理的代码。同时,要注意模糊效果是否会影响其他UI元素,比如图标的显示,需要测试兼容性。 另外,可能需要在系统设置中添加开关,让用户可以选择启用或禁用模糊效果。这涉及到SettingsProvider和SystemUI的配置管理。不过用户暂时可能只需要实现功能,后续再考虑配置选项。 最后,需要验证实现的可行性,比如在Android 13或更高版本上测试,确保模糊效果流畅且不影响系统性能。可能需要参考Android官方文档或已有的开源项目,比如某些自定义ROM中实现类似效果的方法。</think>### 在Framework SystemUI中实现下拉背景实时模糊效果的步骤 #### 1. **核心实现原理** 实时模糊需基于Android的图形渲染机制,通过`RenderEffect`结合硬件加速实现。在Android 12及以上版本中,`RenderNode`支持通过`setRenderEffect`直接应用高斯模糊[^2]。 #### 2. **关键修改位置** - **模糊目标容器**: 下拉状态栏的根布局通常位于`NotificationPanelView.java`(路径:`frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/`) - **模糊逻辑注入**: 在视图绘制时动态应用模糊效果,需重写`dispatchDraw`或`draw`方法,例如: ```java // 伪代码示例(NotificationPanelView.java) @Override public void draw(Canvas canvas) { // 启用离屏缓冲 RenderNode renderNode = new RenderNode("blurTarget"); renderNode.setPosition(0, 0, getWidth(), getHeight()); Canvas offscreenCanvas = renderNode.beginRecording(); super.draw(offscreenCanvas); renderNode.endRecording(); // 应用高斯模糊(半径8px) renderNode.setRenderEffect(RenderEffect.createBlurEffect(8f, 8f, Shader.TileMode.CLAMP)); canvas.drawRenderNode(renderNode); } ``` #### 3. **性能优化** - **动态采样率**:根据滑动状态调整模糊半径(如展开时模糊增强,收起时降低) - **层级限制**:仅模糊背景图层,避免对子视图重复处理 - **硬件加速标记**:在`AndroidManifest.xml`中为SystemUI启用`android:hardwareAccelerated="true"` #### 4. **兼容性适配** 需在`StatusBar.java`中监听窗口属性变化: ```java // 监听窗口背景透明度变化 window.getAttributes().alpha.addListener((observable, oldValue, newValue) -> { updateBlurIntensity(newValue); }); ``` #### 5. **实现效果验证** 可通过ADB命令强制触发重绘: ```bash adb shell service call ActivityTaskManager 1599295570 ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值