尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Android 15 Launcher3文件夹图标定制:从FolderIcon绘制到RRO overlay实践
最近在给一台 Android 15 的定制设备做桌面二次开发客户提了一个听起来很简单的需求把 Launcher3 原生文件夹的 icon 换成他们自己的品牌图标。我当时心想这无非就是改一张图片资源的事结果真正动手才发现Launcher3 里的文件夹图标根本不是一张普通位图而是一个由FolderIcon这个自定义 View 实时绘制的复合组件。你要是不理解它内部的绘制时序改出来的效果要么不生效要么能把文件夹预览搞成一团浆糊。这篇文章不是教科书式的源码分析而是把我这几周踩坑、试错后的完整思路和可复现步骤整理出来。内容主要围绕 Android 15 上的 AOSP Launcher3 展开适合正在做系统 ROM 定制、桌面二次开发或者需要在 SDK 里模拟类似效果的工程师。不管你是想直接换一张静态图片还是想给文件夹加一个动态角标下面这套方案都够用。1. 项目概述与需求拆解1.1 原生文件夹图标到底长什么样要改东西得先知道原厂做的什么。Android 桌面Launcher3里“文件夹”不是一个普通应用图标而是一个独立的自定义 View。你在桌面长按一个图标拖到另一个图标上系统会创建FolderIcon默认在桌面上显示为一个半透明的圆角背景背景里错落堆叠着几个子图标的小预览下方跟着“文件夹”三个字的文本标签。所以用户所说的“文件夹的 icon”严格讲包含两层含义一是外层那个圆角背景形状二是背景里那一组子图标的实时预览。我们做定制时经常遇到的诉求也分三种只换背景形状和颜色保留子图标预览把整个文件夹图标替换成一张独立品牌图比如静态 PNG在原有预览基础上增加角标、动态时钟、数字未读等附加信息。这三种诉求的改动深度完全不一样。第一种用资源覆盖就能解决第二种可能需要动绘制逻辑第三种则需要理解 View 的生命周期和绘制顺序。很多刚开始接触 Launcher3 的开发者一上来就想把FolderIcon直接替换成ImageView结果文件夹打不开了因为长按、拖拽、开合动画全都挂在这个自定义 View 上。1.2 动手前先回答三个问题我建议在写任何代码之前先拿下面这张表给需求方对一遍。很多时候客户嘴上说“换个 icon”实际心里想要的是“icon 上带个角标”或者“文件夹不要显示预览只显示 logo”。需求描述真实改动推荐实现方式换背景颜色/圆角替换 drawableRRO overlay换一张固定 logo修改 FolderIcon 绘制资源RRO overlay 或布局覆盖加未读角标改动 FolderIcon 绘制逻辑源码修改不要预览只显示单图禁用预览层源码修改 布局覆盖预览图和背景都要适配暗色模式需要多套资源overlay night qualifier回答这三个问题基本能定方案文件夹是否需要保留“子图标实时预览”如果保留就不能简单用普通ImageView盖掉图标是静态的还是跟着系统动态变化的比如应用内数字、时间动态需求必须走代码是单独一台调试机还是要批量出厂的设备批量场景强烈建议用 RRO overlay不用改 AOSP 原包。2. Launcher3 文件夹图标的生成机制解析2.1 FolderIcon 和 PreviewBackground 的分工AOSP 里 Launcher3 的相关代码在packages/apps/Launcher3目录文件夹这块核心有两个类com.android.launcher3.folder.FolderIcon和com.android.launcher3.folder.PreviewBackground。FolderIcon是整个文件夹的入口它负责处理触摸事件、长按拖拽、打开动画、数据绑定。你可以理解成它就是一个容器本身不“画”文件夹的视觉内容真正的背景绘制由PreviewBackground一个自定义 Drawable 实现完成子图标的排列则依赖FolderPreview那套逻辑。这个分工非常关键。因为当你想要改图标的时候不能只盯着FolderIcon还要知道哪个阶段画背景、哪个阶段画子图标。一般的绘制顺序是先画PreviewBackground的圆角背景再计算子图标的缩放和位置并绘制最后画文件夹选中态、焦点态之类的状态图层。如果自定义绘制代码插在错误的位置轻则被背景盖住重则把触摸区域弄乱。2.2 资源与布局folder_icon.xml 拆解除了 Java 代码Launcher3 的文件夹图标也离不开布局资源。系统默认的布局文件在packages/apps/Launcher3/res/layout/folder_icon.xml附近里面通常是一个自定义FolderIcon标签内部会包含一个用于显示文本的FolderNameView或BubbleTextView以及存放背景资源的 ImageView 逻辑。这里最容易踩的坑是很多新手想当然地认为folder_icon.xml里应该有一个可以直接换 src 的ImageView于是把FolderIcon标签改成ImageView结果编译出来桌面直接崩溃。原因前面也说了FolderIcon是一个带有完整触摸反馈和文件夹逻辑的自定义 View不是普通图片容器。真要替换布局也必须保留FolderIcon根标签只去动它内部的子元素或 drawable 引用。另外要注意 density 目录。Launcher3 自带的背景资源分布在drawable-anydpi、drawable-mdpi、drawable-xxxhdpi等目录如果你随随便便放一张 48x48 的 PNG 到drawable-xxxhdpi在低分辨率设备上会显得很突兀。系统里的图标网格尺寸是按 dp 算的最好用 vector drawable 或者按不同 density 出一套位图。2.3 Android 15 的图标约束adaptive icon 与主题图标Android 15 已经全面普及了 Adaptive Icon自适应图标机制系统桌面会给每个应用图标套一个前景、背景分层模型。文件夹作为内部的组合图标虽然不直接参与 adaptive icon 动态遮罩但它会读取子应用图标的 adaptive 图层用来做预览。换句话说文件夹预览里的子图标长什么样取决于那些子应用本身是否提供了适配层。如果你的定制方案里某些应用没有自适应图标只是老式 PNG那么文件夹预览里那个图标可能会被放大、裁切或者出现背景色不统一的情况。这个问题不一定是你改文件夹代码导致的而是上游图标数据有问题。排查时要先分清。另外Android 15 对主题图标themed icon越来越激进如果系统开启主题化图标桌面上很多图标会变成单色雾面样式。文件夹背景如果用了固定颜色资源在主题化模式下可能和附近图标格格不入。所以做定制 UI 时我习惯让背景颜色跟随系统强调色或壁纸取色而不是写死一个品牌色。3. 修改文件夹图标的三种落地方式3.1 方案 ARRO overlay 替换静态资源推荐给批量设备RRORuntime Resource Overlay是 Android 官方推荐的资源覆盖方式特别适合不想改 AOSP 原生包、希望产品线单独定制桌面风格的场景。它能在不修改Launcher3.apk的前提下替换其中的资源 ID 所指向的 drawable、layout、color 甚至字符串。在 Android 15 的设备上overlay 的编译通常用 Soong 的新写法核心是一个runtime_resource_overlay模块再配一个 AndroidManifest声明targetPackagecom.android.launcher3。我的经验是优先覆盖folder_background这类明确的 drawable 资源而不是覆盖整个folder_icon.xml布局。因为布局里包含太多 View 的引用和 id一旦覆盖文件版本和原固件对不上很容易出现 id 错乱、空指针。overlay 的另一个好处是能动态禁用、启用。测试阶段出问题一条命令就能摘掉 overlay不必重新编译整个系统。这对多机种并行调试帮助很大。3.2 方案 B直接改 FolderIcon 绘制逻辑适合动态/品牌需求如果要做的是带未读数字的角标或者根据不同商业模式切换文件夹封面的联动效果资源 overlay 就无能为力了。这时候只能改源码。改代码不要一上来就重写FolderIcon。正确做法是先找到它的onDraw(Canvas)或draw(Canvas)方法看清现有绘制链路然后在“最后一层”追加自己的绘制内容。比如你画一个品牌 logo 或者角标只需要在super.onDraw(canvas)之后手工设置Drawable.setBounds再draw(canvas)即可。如果渠道包要动态控制显示哪张 logo可以给FolderIcon增加一个入口方法由Launcher的配置层传入参数。这里要提醒一句请不要在这个类里写太复杂的业务逻辑。FolderIcon是桌面进程里高频绘制的对象桌面滑动、文件夹开关、图标拖拽都会触发它重绘。任何耗时操作放在onDraw里都会卡桌面。3.3 方案 C在布局层插入自定义 View兜底方案有时候既不想大改源码又不想单变量整包 overlay可以走“半资源”路线通过 overlay 覆盖folder_icon.xml布局在不改变FolderIcon根标签的前提下往布局里多塞一个自定义 View 或ViewStub。这个 View 专门负责盖一层自定义图标在预览之上。这个方案比改绘制逻辑安全因为不动 Java 代码只在布局上叠加 UI。但坑也很明显View 之间的层级、触摸穿透、事件分发必须处理对。如果你加的 View 盖得比原生FolderIcon还高又默认接收了点击事件那用户点击文件夹图标可能触发你自定义 View 的点击事件而不是打开文件夹。解决办法是在布局中给自定义 View 设置android:clickablefalse并确保它不属于触摸目标。3.4 选型对比实现方式改动范围开发成本灵活度风险RRO overlay 覆盖 drawable只动资源低中低修改 FolderIcon 代码动源码中高高中覆盖布局自定义 View资源和布局中中高中高替换为 ImageView大改布局低低高 不推荐我个人标准能 overlay 解决的尽量不碰源码碰源码之前先看绘制链路实在不行才考虑塞布局层。4. 实操过程从代码到效果的完整步骤4.1 准备 AOSP 与 Launcher3 工程这一步的前提是你已经有一个可编译的 AOSP Android 15 源码环境并且能单独编译 Launcher3。如果还没准备好可以先在已 checkout 源码的机器上执行一次source build/envsetup.sh和lunch your_product然后单独编译 Launcher3 验证环境。source build/envsetup.sh lunch your_product make Launcher3 -j$(nproc)编译产物一般会输出到out/target/product/your_product/system/对应的 APK 路径。这里注意Launcher3 的包名通常是com.android.launcher3但有些厂商会改成别的包名比如com.example.launcher3。你后面做 overlay 时targetPackage必须写对否则 overlay 直接不生效。4.2 通过 overlay 落地静态品牌文件夹图标我在这次的定制项目里最稳的方案就是 RRO overlay。假设我们想给文件夹换一个新的背景 drawable操作如下。先建 overlay 工程目录vendor/yourcompany/overlay/Launcher3FolderIconOverlay/ ├── AndroidManifest.xml ├── Android.bp └── res/ └── drawable/ ├── folder_background.xml └── folder_icon_default.xmlAndroidManifest.xml 里核心是声明 overlay?xml version1.0 encodingutf-8? manifest xmlns:androidhttp://schemas.android.com/apk/res/android packagecom.yourcompany.overlay.launcher3foldericon application android:hasCodefalse / overlay android:targetPackagecom.android.launcher3 android:isStatictrue android:priority1 / /manifestAndroid.bp 按当前 Soong 语法写。注意不要照抄旧版的android_app写法AOSP 新版本里更推荐 runtime resource overlayruntime_resource_overlay { name: Launcher3FolderIconOverlay, package: com.yourcompany.overlay.launcher3foldericon, sdk_version: current, }如果你的产品线需要产物放在 product 分区可以加上product_specific: true之类的属性具体以你那套源码的 Soong 文档为准。drawable 部分假如系统原来用的背景是folder_background你就用同名资源覆盖它。这里我用一个简单 vector 示例vector xmlns:androidhttp://schemas.android.com/apk/res/android android:width48dp android:height48dp android:viewportWidth48 android:viewportHeight48 path android:fillColor#1A73E8 android:pathDataM8,4 L40,4 Q44,4 44,8 L44,40 Q44,44 40,44 L8,44 Q4,44 4,40 L4,8 Q4,4 8,4 Z / path android:fillColor#FFFFFF android:pathDataM12,12 L22,12 L24,16 L36,16 L36,34 L12,34 Z / /vector编译 overlay 后需要把它装到设备的/system/overlay、/product/overlay或/vendor/overlay下具体目录取决于你的编译配置。装完重启桌面进程adb shell am force-stop com.android.launcher3如果 overlay 优先级没错桌面重启后就能看到文件夹背景被替换成了自定义样式。4.3 修改 FolderIcon 的绘制逻辑加入自定义角标当需求变成“文件夹背景之上还要显示一个公司 logo 角标”时overlay 能力就不够了。我这次的场景是需要在文件夹预览图右下角放一个动态数据角标因此要改FolderIcon的绘制代码。先定位到packages/apps/Launcher3/src/com/android/launcher3/folder/FolderIcon.java找到类似onDraw的方法。不要把逻辑写在onLayout之外的地方太靠前。大概的做法是这样Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mBadgeDrawable ! null) { int left getMeasuredWidth() - mBadgeSize; int top getMeasuredHeight() - mBadgeSize; mBadgeDrawable.setBounds(left, top, left mBadgeSize, top mBadgeSize); mBadgeDrawable.draw(canvas); } }mBadgeSize要按屏幕 density 换算不要写死像素mBadgeSize getResources().getDimensionPixelSize(R.dimen.folder_badge_size);这里有两个关键点。第一mBadgeDrawable的创建和更新不要在onDraw里做最好在数据绑定或 UI 刷新入口统一更新否则每帧都更新对象会造成严重的 GC。第二如果你画的角标要跟随子应用图标变化比如未读数量记得在更新完数据后调用invalidate()不然角标不会重绘。4.4 编译、烧录与 Launcher 缓存处理改完源码后重新编译 Launcher3make Launcher3 -j$(nproc)如果只是单独验证可以只 push APKadb root adb remount adb push out/target/product/your_product/system/priv-app/Launcher3/Launcher3.apk /system/priv-app/Launcher3/ adb shell am force-stop com.android.launcher3这里我不建议只在应用管理器里点“清除数据”因为那样会重置桌面布局用户已经排好的图标全没了。正确做法是先强制停止进程必要时再重启系统 UIadb shell pkill -f com.android.launcher3如果图标还是老样子第一怀疑对象是 Launcher3 内部的资源缓存。桌面为了性能会把很多 drawable 缓存成内存态改资源后必须等桌面进程完全杀死再重启。有时am force-stop触发不了 Launcher 自身崩溃重启需要配合adb shell am start -n com.android.launcher3/.Launcher手动拉起。5. 常见问题与排查技巧实录5.1 问题速查表现象最可能原因排查方向overlay 编译通过但不生效targetPackage 写错或优先级冲突检查 AndroidManifest.xml 中的包名桌面图标没有变化Launcher 仍在内存缓存彻底 kill 桌面进程再重启文件夹打开后背景异常背景资源和预览尺寸不匹配检查 PreviewBackground 的圆角参数自定义 View 挡住点击布局层拦截了触摸事件给自定义 View 设置 clickablefalse修改代码后桌面闪退FolderIcon 空指针看 logcat 堆栈定位是哪个字段未初始化图标显示模糊位图密度资源不对使用 vector drawable 或补齐 density 目录深色模式下图标刺眼未配置 night 资源增加drawable-night/覆盖5.2 几个实操中反复踩到的“坑”先说图标缓存。Launcher3 内部有一整套 icon cache各类图标都会被 KeyedCache 存起来。我之前改完folder_background.xml后直接往系统里 push 资源桌面进程没完全重启图标始终是旧的。后来发现不能只am force-stop在某些版本上这个命令不会立刻触发 Launcher 的冷启动必须再把 Launcher 进程杀干净。排查时先确认这一点能省很多时间。再说布局覆盖。如果你选择覆盖folder_icon.xml原文件里的所有id/引用都必须存在否则 inflate 阶段直接抛异常。我遇到过一次folder_icon里引用了folder_icon_name这个 id但我覆盖时把它漏了结果整个桌面崩溃logcat 里只有ResourceNotFound非常难找。最后一个坑是 PreviewBackground 的圆角设置。Android 15 的桌面文件夹背景是有圆角的你自定义 drawable 时如果也写了圆角一定要确保两处圆角半径一致否则会出现“背景圆角变了但预览图还是原来圆角”的割裂感。这块不在folder_background.xml里而在PreviewBackground类里的loadXYZ逻辑中视觉不统一时去那边找。5.3 提交前最后检查集成完功能后我建议至少做一遍下面这组检查在横屏、竖屏各切换一次文件夹图标是否正常重绘创建一个新文件夹、删除一个旧文件夹预览刷新是否正常把两个带 adaptive icon 的应用拖入文件夹确认预览层没有裁切在深色模式、高对比度模式下分别看一遍背景和文字是否可辨识连续开合文件夹 20 次以上确认没有内存泄漏或明显掉帧如果用了 overlay执行adb shell cmd overlay list确认 item 状态是[x]。这些检查看起来基础但很能暴露问题。文件夹图标这种高频 UI 改动最怕只在你测试的那一条路径上看起来正常换个场景就翻车。最后再分享一个个人心得改 Launcher3 文件夹图标难点不在“会画一张图”而在“搞清楚这张图是哪个环节画出来的”。AOSP Launcher3 经过这么多版本迭代绘制链路已经非常稳定它不会因为你要换一张 icon 就给你一张简单的替换口子。遇到需求先按“资源覆盖 → 绘制追加 → 布局叠加”的顺序评估能少走很多弯路。按这个顺序动手大部分定制需求都能在一天内完成第一版可演示效果后面再慢慢打磨细节。
RELATED

相关推荐

5G入网失败的隐形杀手:晶振ppm精度与频偏全解析

5G入网失败的隐形杀手:晶振ppm精度与频偏全解析

前阵子公司一款 5G CPE 在客户现场低温环境下频繁入网失败,半天连不上 5G 网络。大家一开始怀疑天线匹配、怀疑协议栈,排查了一整周,最后发现根因居然是一个“毫不起眼”的参数——晶振的 ppm 超标。这个问题在 4G 时代几乎没人正眼瞧过&…

📅 2026/10/22 22:15:48
MySQL数据迁移三大利器:LOAD DATA INFILE、OUTFILE与mysqldump实战解析

MySQL数据迁移三大利器:LOAD DATA INFILE、OUTFILE与mysqldump实战解析

最近在整理一批历史数据迁移方案,正好把MySQL里几种常用的数据导入导出方式从头到尾梳理了一遍。 LOAD DATA INFILE 、 SELECT INTO OUTFILE 、 mysqldump 这三个工具,平时各自用起来都不难,但真正要在生产环境里高效配合、避免踩坑&am…

📅 2026/10/22 22:15:48
A2A协议与MCP互补:多智能体协作与工程实践

A2A协议与MCP互补:多智能体协作与工程实践

最近我接手了一个内部AI平台升级的活儿,要把散落在不同工程团队手里的几个智能体串成一个自动化处理链路。按理说都是智能体,但A团队用LangGraph,B团队自研状态机,C团队直接依赖某个外部托管服务,想让它们互相配合&…

📅 2026/10/22 22:10:45
MORE NEWS

更多资讯

📰

直播美颜SDK性能优化全解析:架构、算法与踩坑实录

开篇先聊一个很现实的问题:直播场景下的美颜 SDK,和拍照修图里的美颜,看起来都是“把人变好看”,但技术选型几乎完全是两条路。拍照可以离线慢慢算,一张图处理几百毫秒用户都不觉得慢;直播是 30 帧每秒连续…

📰

RAG检索优化实战:分块、混合召回与重排核心方案

做 RAG 这行当,demo 阶段人人都觉得“已经成了”,真正上线跑一两周,才会撞见那个经典场面:模型能力没问题,prompt 也调了十几版,可用户一问到具体业务,回复要么“抱歉,我没有找到相关…

📰

大模型Agent开发全流程:模型选型、工具设计、护栏与编排

“真香警告”这四个字放在标题里,是有原因的。我一开始对Agent这类项目是持谨慎态度的,觉得无非是把模型调用包装得更花哨了一些,大概率又是概念大于实用。直到我亲手把一个会调外部工具、能分步骤干活、出错了还能自我修正的小东西跑起来之后…

📰

SDFT:缓解灾难性遗忘的选择性微调方法,从核心设计到落地实践

如果你经常微调模型,一定遇到过这种场景:模型在第一个任务上学得很好,准确率已经稳定了,然后你用第二批数据让它学习新技能,训练完成后新任务的表现上去了,但回过去一测旧任务,效果明显下滑。我…

📰

模型越强越需要人:Human in the loop落地指南

Human in the loop 人在回路中,这个概念在我第一次做大型语言模型项目的时候还不太被当回事。当时团队里很多人的想法是:模型已经这么强了,把人的反馈接进来,不是拖慢效率吗?但实际跑了几轮之后,所有人都不…

📰

融合DWA与速度障碍法的移动机器人动态避障实践

我们做移动机器人底盘导航调试那几年,最头疼的从来不是静态地图上的路径规划,而是动态障碍物。明明全局路径规划得好好的,车一开起来,碰到行人横穿、其他机器人迎面过来,传统的动态窗口法(DWA)经…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬