ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Compose Multiplatform 三方库 compose-icons(Octicons)的 OpenHarmony 鸿蒙化适配实战

Compose Multiplatform 三方库 compose-icons(Octicons)的 OpenHarmony 鸿蒙化适配实战 Compose Multiplatform 三方库 compose-iconsOcticons的 OpenHarmony 鸿蒙化适配实战fill 模式图标包验证一套 shim 平移踩中 ArkUI 椭圆弧大坑库版本compose-iconsOcticons 包414 个图标验证环境Compose Multiplatform 生态 / Kotlin 2.2.21-1.0.0鸿蒙定制版DevEco Studio 26.0.0DevEco 模拟器HarmonyOS 7.0.0API 26我之前Tabler Icons 适配验证了「shim 记录几何数据 → JSON → ArkUI Path() 渲染」这条路径的可行性但 Tabler 是stroke 模式图标线条描边文末 FAQ 留了一个问题fill 模式实心填充图标包怎么办本文就是那个问题的答案——把同一套 shim 平移到 GitHub 官方图标库Octicons414 个图标16px 24px 双尺寸验证「数据定义类库」适配路径对填充型图标包的通用性。结论先行414 个 Octicons 图标源码零修改整条链路Kotlin/Native.so→ NAPI → ArkTS → ArkUIPath()fill 渲染最终跑通。但与 Tabler 版不同本次踩中一个 Tabler 没暴露的大坑——ArkUIPath().commands()不支持 SVG 椭圆弧命令A/a而 Octicons 几乎每个图标都靠它画圆形/圆角导致首版渲染里所有含圆弧的图标全部变形Search 放大镜变成实心圆。最终在 shim 的arcTo出口处按 SVG 1.1 规范把椭圆弧展平成多条三次贝塞尔C曲线解决——上游图标源码依然零修改改动全部收敛在 shim 一个文件里架构的可复用性反而得到了更扎实的验证。先睹为快DevEco 模拟器实测414 个 Octicons 图标16px 24px 双尺寸由 Kotlin/Native 侧导出几何数据、ArkUI Path() 按 fill 模式渲染——Search 放大镜空心圆镂空、CheckCircle/PlusCircle 等圆形图标全部正确一、适配目标与整体链路目标在鸿蒙模拟器里跑一个 ArkTS 应用页面加载时真实调用Kotlin/Native 里的 compose-icons Octicons 图标构建代码把精选图标的几何数据SVG path 字符串 fill 颜色 alpha拉到 ArkTS用 ArkUIPath()组件按fill 模式渲染成图标网格——验证同一套 shim 对填充型图标包的通用性。整体链路与 Tabler 版完全一致仅数据模式不同ArkTS (Index.ets) │ import icons_napi from libicons.so ▼ NAPI 调用 getFeaturedIcons() / getIconCount() libicons.so ← C NAPI 薄层entry/src/main/cpp/napi_init.cpp │ ▼ extern C 调用 libohosicons.so ← Kotlin/Native (ohosArm64 / ohosX64) │ ▼ octicons 模块 → compose-icons 上游源码 自研 androidx.compose.ui shim零修改适配目标的最终效果*首屏56px 图标按 4 列排布Search 放大镜、圆形勾选/关闭图标镂空清晰图标名与 FEATURED 列表一致*二、Octicons 与 Tabler 的差异为什么值得单独适配Octicons 是 GitHub 的官方图标库在 compose-icons 里的形态与 Tabler 相同Kotlin 源码 ImageVector DSL但数据模式有三个关键差异维度Tabler IconsOcticons渲染模式stroke线条描边stroke SolidColor(...), fill nullfill实心填充fill SolidColor(Color(0xFF000000)), stroke null尺寸体系单一 24px viewport16px 24px 双尺寸并存Home16/Home24图标数量185 个当前收录414 个16/24 两版合计几何命令纯直线/贝塞尔M/L/C/Q/Z零arcTo**大量arcTo/arcToRelative400 处**画圆形/圆角这三个差异恰好覆盖了 Tabler 版 FAQ 里预留的全部扩展点fill 模式Tabler 版 ArkTS 用stroke()fillOpacity(0)渲染线条Octicons 必须反过来——fill()strokeOpacity(0)渲染实心形状。shim 的PathData早已记录了fill: Color?/fillAlpha字段JSON 契约只需把这两个字段真正用起来。双尺寸Octicons 的AllIcons里同一个图标有 16px 和 24px 两个版本viewport 不同16 vs 24序列化时各自独立成条目——天然验证了 JSON 契约对多 viewport 的兼容性。数量翻倍414 个图标的 JSON 达 154KBTabler 精选 185 个约 67KB验证链路在更大数据量下的表现——实测依然毫秒级。第四个差异是本次踩坑的根源Tabler 的 185 个图标里没有任何一个用到arcToSVGA/a椭圆弧命令而 Octicons 几乎每个图标都用它画圆形和圆角——这个差异在 Tabler 版完全没暴露到 Octicons 才引爆详见第五章踩坑。三、工程结构octicons-ohos-demo/ ├── octicons/ # 库模块shim 上游图标源码 │ └── src/commonMain/kotlin/ │ ├── androidx/compose/ui/ # shim仅 ImageVector.kt 一处改动椭圆弧展平 │ │ ├── graphics/Color.kt # 与 Tabler 版相同 │ │ ├── graphics/vector/ImageVector.kt # ★ 本次唯一改动文件arcTo → C 曲线展平 │ │ ├── graphics/vector/Path.kt # 与 Tabler 版相同 │ │ └── unit/Dp.kt # 与 Tabler 版相同 │ ├── compose/icons/__Octicons.kt # AllIcons(414) / AllIconsNamed 注册表 │ └── compose/icons/octicons/*.kt # 414 个上游图标文件零修改 ├── example/ │ ├── nativeApp/ # Kotlin/Native 桥接层 → libohosicons.so │ │ └── src/ │ │ ├── commonMain/kotlin/IconBridge.kt # FEATURED 列表 fill 模式 JSON │ │ └── ohosMain/kotlin/IconExport.kt # CName 导出与 Tabler 版相同 │ └── ohosApp/ # ArkTS 鸿蒙应用bundleName: com.example.octiconsdemo │ └── entry/src/main/ │ ├── cpp/napi_init.cpp # C NAPI 薄层与 Tabler 版相同 │ ├── ets/pages/Index.ets # ArkTS fill 模式渲染 │ └── libs/{arm64-v8a,x86_64}/ # 双 ABI so4.6MB / 4.1MB └── settings.gradle.kts / build.gradle.kts与 Tabler 版的差异点只有四处库模块名octicons、IconBridge.kt的 FEATURED 列表与 JSON 序列化fill 字段、Index.ets的渲染模式以及shim 的ImageVector.kt一处椭圆弧展平——C NAPI 薄层、CName出口全部原样复用。四、适配过程三个关键步骤4.1 Gradle 工程配置与 Tabler 版同构鸿蒙定制工具链2.2.21-1.0.0的pluginManagement仓库配置不变模块名从tabler-icons换成octicons// settings.gradle.ktspluginManagement{repositories{maven(https://maven.eazytec-cloud.com/nexus/repository/maven-public/)// 必须第一位mavenCentral()gradlePluginPortal()}}dependencyResolutionManagement{repositories{/* 同上 */}}rootProject.nameocticons-ohos-demoinclude(:octicons,:example:nativeApp)4.2 核心fill 模式的 JSON 序列化IconBridge上游图标源码零修改桥接层新增 fill 字段。Octicons 的图标定义长这样注意fill有值、stroke null且大量使用arcToRelativepublicvalOcticons.Search24:ImageVectorget(){_search24Builder(nameSearch24,defaultWidth24.dp,defaultHeight24.dp,viewportWidth24.0f,viewportHeight24.0f).apply{path(fillSolidColor(Color(0xFF000000)),strokenull,...,pathFillTypeEvenOdd){moveTo(14.53f,15.59f)arcToRelative(8.25f,8.25f,0.0f,true,true,1.06f,-1.06f)// ← 放大镜外圆lineToRelative(5.69f,5.69f)...moveTo(2.5f,9.25f)arcToRelative(6.75f,6.75f,0.0f,true,true,11.74f,4.547f)// ← 放大镜内圆镂空...close()}}.build()return_search24!!}IconBridge.iconToJson()相比 Tabler 版新增两个字段funiconToJson(name:String,icon:ImageVector):StringbuildString{append({)append(\name\:\).append(name).append(\)append(,\viewportWidth\:).append(icon.viewportWidth)// 16 或 24双尺寸append(,\viewportHeight\:).append(icon.viewportHeight)append(,\paths\:[)icon.paths.forEachIndexed{i,p-if(i0)append(,)append({)append(\d\:\).append(escape(p.svgPath)).append(\)append(,\fill\:\).append(colorHex(p.fill)).append(\)// 新增fill 颜色append(,\fillAlpha\:).append(p.fillAlpha)// 新增fill alphaappend(,\strokeWidth\:).append(p.strokeLineWidth)// ... strokeCap / strokeJoin / fillRuleappend(})}append(])append(})}colorHex把Color(0xFF000000)转成#000000commonMain 里没有String.format手动按位转 hex。FEATURED 列表精选 207 个图标16px 与 24px 混排覆盖导航、Git 工作流、文件、安全、品牌LogoGithub/MarkGithub/Octoface等分类。一个 Kotlin 语义细节Octicons 的图标属性是Octicons对象的扩展属性val Octicons.Home24桥接层引用时必须写Octicons.Home24而非裸Home24且需要import compose.icons.AllIcons同样是扩展属性——这是初次编译报 200 个receiver type mismatch的原因。4.3 ArkTS 渲染fill 模式 居中缩放Index.ets的渲染核心与 Tabler 版对称——fill 与 stroke 互换。另一个细节是缩放Kotlin 导出的 path 坐标是相对 viewport0~16 或 0~24的必须先按 viewport 尺寸布局、再绕中心缩放到目标像素尺寸否则scale默认绕左上角会导致放大后偏移// Tabler 版stroke 模式Path().commands(...).fillOpacity(0).stroke(#4FC3F7).strokeWidth(this.strokeScale(icon))// Octicons 版fill 模式 居中缩放Path().commands(icon.paths.map((p:PathJson)p.d).join( )).fill(tint)// JSON 里的 fill 颜色回退主题色.fillOpacity(this.iconFillAlpha(icon))// JSON 里的 fillAlpha.strokeOpacity(0).width(icon.viewport)// 先按 viewport 尺寸布局.height(icon.viewport).scale({x:sizePx/icon.viewport,y:sizePx/icon.viewport,centerX:50%,centerY:50%})// 绕中心放大到 56px不偏移C NAPI 薄层napi_init.cpp、CName出口IconExport.kt与 Tabler 版逐字节相同——C ABI 符号名OhosIconsFeatured/OhosIconsCount/OhosIconsLastError/OhosIconsFree不变so 名libohosicons.so不变两个应用甚至可以并排装在同一台模拟器上bundleName 分别为com.example.composeiconsdemo/com.example.octiconsdemo。五、踩坑记录4 个第 1 个是本次核心坑现象解法ArkUIPath.commands不支持 SVGA/a椭圆弧命令核心坑首版渲染Search 放大镜变成实心圆CheckCircle/XCircle/PlusCircle 等所有含圆弧的图标全部变形纯直线图标Check/X/Plus/Dash正常在 shim 的arcTo/arcToRelative出口处按 SVG 1.1 规范endpoint → center 参数化把椭圆弧展平成多条三次贝塞尔C曲线导出的 JSON 只剩M/L/C/Q/Z——ArkTS 侧零改动扩展属性 receiver 丢失桥接层裸引用Home24编译报 200 个receiver type mismatchOcticons 图标是Octicons对象的扩展属性引用必须带 receiverOcticons.Home24AllIcons同理需单独 import部分图标无 24 版Unresolved reference LogoGithub24等 14 个报错Octicons 部分图标只有 16px 版LogoGithub/MarkGithub/Markdown/ThreeBars…FEATURED 列表改用 16 版ArkUIscale默认绕左上角图标放大到 56px 后在卡片里偏移、不居中先按 viewport 尺寸布局再.scale({ ..., centerX: 50%, centerY: 50% })绕中心缩放椭圆弧展平的核心实现shim 唯一改动为什么 Tabler 没踩这个坑因为它的 185 个图标没有任何一个用arcTostroke 线条图标用直线/贝塞尔就够了而 Octicons 的圆形、圆角全靠椭圆弧——arcTo/arcToRelative在 414 个图标文件里出现了400 处。当Path().commands(...)解析到A命令时中断后续的内圆镂空路径被丢弃EvenOdd 填充退化成只画外轮廓——放大镜就成了实心圆。修复方案是改 shim 而不是改上游图标414 个文件零修改的承诺不破在PathBuilder的arcTo/arcToRelative里直接输出贝塞尔曲线。算法是 SVG 1.1 规范附录 F.6 的标准转换——endpoint 参数化转 center 参数化再按每段 ≤90° 切成多条三次贝塞尔// ImageVector.kt —— PathBuilder 内部funarcTo(rx:Float,ry:Float,theta:Float,isMoreThanHalf:Boolean,isPositiveArc:Boolean,x1:Float,y1:Float):PathBuilder{appendArcAsCubics(rx,ry,theta,isMoreThanHalf,isPositiveArc,x1,y1)currentXx1;currentYy1returnthis}privatefunappendArcAsCubics(rx,ry,xAxisRotationDeg,largeArc,sweep,endX,endY){// 1. (x1,y1) → (x1,y1) 变换到旋转前坐标系// 2. 半径过小则按 sqrt(lambda) 放大校正// 3. 求中心点 (cx, cy) → 变回原坐标系 (cx, cy)// 4. 求起始角 θ1 与扫掠角 Δθ按 sweep 修正到正确象限// 5. 按 ≤90° 分段每段用一条三次贝塞尔逼近valt(4f/3f)*tan(delta/4f)// 控制点系数// 单位圆控制点 → 缩放 旋转 平移映射回椭圆curveTo(mapX(p1x,p1y),mapY(p1x,p1y),mapX(p2x,p2y),mapY(p2x,p2y),mapX(p3x,p3y),mapY(p3x,p3y))}改完后导出的 JSON 里 Search24 的d字段从M14.53 15.59A8.25 8.25 ...含A变成纯M...C...C...C...Z贝塞尔展开ArkUI 原样吃下。这个改动对 Tabler 版零影响它本来就不含arcTo且对未来的 Feather/FontAwesome/Material 等图标包自动生效——圆形元素多的图标包都能直接受益。六、运行效果DevEco 模拟器实测Demo 深色图标网格页顶部标题 状态栏图标数/耗时 图标按 56px 四列排布fill 模式实心渲染。首屏加载即拉取全部精选图标*首屏精选 207 / 共 414 图标30ms 拉取Search 放大镜空心圆镂空、HomeFill 实心、CheckCircle/PlusCircle 圆形镂空清晰——椭圆弧展平修复生效*向下滚动查看 Git 工作流与品牌图标区*中部Repo 系列、LogoGithub/MarkGithub 品牌图标、Discussion/Organization 等社区图标实心填充效果清晰*继续滚动到底部时间与表情图标区*底部Stopwatch/Trophy 等时间奖杯图标、Smiley/Heart 等表情图标、Octoface 章鱼猫几何数据与上游完全一致*真实性验证hilog——页面加载有日志铁证ComposeIconsNapi: getIconCount414 ComposeIconsNapi: getFeaturedIcons: count207 ComposeIconsNapi: getFeaturedIcons len157772 head[{name:Home,viewportWidth:24.0,viewportHeight:24.0,paths:[{d:M11.03...渲染正确性验证——椭圆弧展平前后对比图标修复前含A命令修复后贝塞尔展开Search实心圆镂空丢失放大镜空心圆 斜柄CheckCircle实心圆圆形轮廓 内部对勾XCircle / PlusCircle / NoEntry实心圆圆形镂空 内部符号Check / X / Plus / Dash纯直线正常正常不受影响每个像素都来自 Kotlin/Native 侧导出的真实几何数据非 mock——414 个图标的注册表全量可访问FEATURED 精选 207 个单次拉取 30ms。七、FAQQ1这次还是shim 零修改吗不是了。Tabler 版的四文件 shim 在 Octicons 工程里改了一个文件ImageVector.ktarcTo/arcToRelative从直接透传A/a命令改为椭圆弧展平成贝塞尔曲线。其余三文件Color.kt/Dp.kt/Path.kt依然零修改。这次改动恰恰说明shim 的抽象边界是对的——fill/stroke 模式、双尺寸都能零改动吃下唯一的缺口是 ArkUI 渲染端对 SVG 命令集的支持度缺A而补齐这个缺口只需要在 shim 的几何出口处做一次命令降级上游 414 个图标文件和 ArkTS 渲染侧都不用动。Q216px 和 24px 双尺寸怎么处理各自独立成 JSON 条目viewport 16 或 24ArkTS 侧先按 viewport 尺寸布局、再绕中心缩放到 56px 显示尺寸——同一套渲染代码对任意 viewport 通用。Q3fill 颜色都是黑色JSON 里带颜色有意义吗有。上游Color(0xFF000000)是图标的默认色真实业务里会按主题重着色。JSON 契约保留颜色字段后多色图标包如 FontAwesome 的品牌色可以零改动接入。Q4414 个图标全序列化会怎样当前 FEATURED 精选 207 个154KB JSON30ms。全量 414 个约 300KB单次拉取依然是毫秒级如果追求极致可以走编译期预生成 rawfile路线见 Tabler 版扩展方向。Q5下一个图标包还需要做什么按本次经验平移一个新图标包Feather / FontAwesome / Material…的工作量 换 FEATURED 列表 确认渲染模式fill 或 stroke ArkTS 三行渲染参数。shim含椭圆弧展平、NAPI、出口层全部不动——而且经过 Octicons 这次shim 对含大量圆弧的图标包也验证过了后续平移的意外成本更低。八、总结与参考Octicons 适配把 Tabler 版的数据定义类库路径从单模式验证推进到模式覆盖验证stroke 与 fill 两种渲染模式、16/24 双尺寸体系、414 个图标的数据量、400 处椭圆弧几何同一套架构全部吃下。更重要的是本次踩中并修复了 Tabler 没暴露的ArkUIA命令坑——这恰好证明了「shim 记录几何 → JSON 契约传输 → ArkUI 原生渲染」这条链路的可调试性与可收敛性渲染端的命令支持缺口可以在 shim 的几何出口处一次性补齐椭圆弧 → 贝塞尔降级而不需要触碰上游任何一个图标文件。至此 compose-icons 的适配方法论已经收敛shim含命令降级记录几何 → JSON 契约传输 → ArkUI 原生渲染。剩余的图标包Feather、FontAwesome、Material、Simple Icons…都是这条路径上的重复劳动可以按需批量平移。上游库https://github.com/DevSrSouza/compose-icons鸿蒙定制仓库https://maven.eazytec-cloud.com/nexus/repository/maven-public/OpenHarmony 三方库社区https://atomgit.com/oh-tpc欢迎加入 KMPCMP 鸿蒙社区https://atomgit.com/CPF-KMP-CMP本问适配源码仓库https://atomgit.com/oh-tpc/compose-icons
返回列表