本文记录 PHFComposeBarView 接入 OpenHarmony 的完整过程,覆盖 ObjC 界面库盘点、ArkTS/ArkUI 全量移植、HAR 库封装、ArkUI 真机页面、HAP 构建、签名和真机验收。

PHFComposeBarView 是与算法、并发无关的纯 UI 库——iOS 7 Messages.app 输入栏的精确复刻。本次适配把上游全部布局常量、颜色与交互行为用声明式 ArkTS 重写为 HAR 库 phfcomposebarview,ArkUI 页面作为真机验收宿主,度量按 iOS pt == ArkUI vp 一比一搬运,不引入任何原生代码。

项目地址: AtomGit/oh-tpc/PHFComposeBarView

上游仓库: fphilipe/PHFComposeBarView

开发工具: 华为云码道

环境安装: 环境安装指南

一、背景

1.1 为什么做 OpenHarmony 平台 KMP/CMP 适配

PHFComposeBarView 为 UIKit 提供消息输入栏:文本框、占位符、左侧工具按钮、右侧 Send 按钮、字数计数器、自动增高、毛玻璃背景。上游用 Objective-C 写成,frame 手算、[UIView animateWithDuration:] 手动驱动、NSNotificationCenter 广播高度变化。

OpenHarmony 应用同样需要这根输入栏,但上游代码没有任何可在 OHOS 上复用的形态:没有 Kotlin commonMain 可以补 ohosArm64(),没有 C ABI 可以包 NAPI。如果只把示例页面改成 ArkTS 而不移植库本体,页面可以显示,却无法证明库的全部行为规格(增高公式、计数器显隐、按钮启用条件、按压反馈、事件广播)真的落进了 HarmonyOS 运行时。

障碍具体问题
语言不同Objective-C / UIKit 无法编译到 OHOS ARM64,也没有互操作通道。
范式不同上游是命令式(frame 计算 + 手动动画),ArkUI 是声明式(状态驱动布局)。
度量体系布局全部钉在 iOS pt 上(44 / 8 / 25 / 5.25 / 0.5…),需要无损搬运到 vp。
事件模型delegate 协议 + NSNotification 在 ArkTS 没有直接对应物。
字体度量上游运行时测量单行高度与行高增量,ArkUI 需要等价机制。
交付链路复杂HAR 库、Demo、HAP、签名和 hdc 都要单独验收。

因此,本项目把边界放在「上游源码即规格书」:Classes/ 三个 ObjC 类的每个常量、每个触发时机、每个回调签名,都映射为 ArkTS 实现并逐条验收;业务语义仍由同一套规格负责,ArkTS 只负责用声明式 UI 把它表达出来。

1.2 库提供的能力

适配后的 ComposeBarView 组件提供上游全部能力:

  • 自动增高:文本换行时输入栏跟随长高,键入瞬时变化;默认底边固定、向上生长;
  • maxLinesCount / maxHeight:maxHeight = 44 + (行数 - 1) × 20,与上游公式一致,到顶后输入框内部滚动;
  • 字数计数器:maxCharCount > 0 且文本 ≥ 2 行时显示 count/max,trim 后计数,超限只计数不拦截;
  • Send 主按钮:粗体 17fp,正常 #007AFF / 禁用 #909094,文本非空且启用才可点,按压立即降至 0.2 透明度、松开 0.2s 动画恢复;
  • 工具按钮:25×25、底边距 9,出现时输入区右侧让位(对应上游右移 33pt);
  • 事件广播:高度变化的 Will/Did 回调 + emitter 事件,供使用方同步避让布局。

ComposeBarView 的公开面包括:

字段作用
text双向绑定文本,对应上游 text 属性。
placeholder / placeholderColor占位符内容与颜色(#C8C8CC)。
maxCharCount计数器上限,0 表示不显示。
maxLinesCount / maxHeight高度上限的两种表达,公式互算。
inputEnabled同时禁用输入框与两个按钮(上游 enabled)。
buttonTitle / buttonTintColorSend 按钮标题与着色。
utilityButtonVisible / utilityButtonSymbol工具按钮开关与 SymbolGlyph 资源。
onButtonPressed 等回调对应上游四个 delegate 方法与 textViewDidChange 转发。

1.3 实现适配

维度要求
规格复用布局常量、颜色、行为公式全部来自上游 PHFComposeBarView.m,一比一搬运。
平台目标纯 ArkTS/ArkUI,无 C++ / CMake / NAPI / Kotlin 依赖。
组件封装以 HAR 库 phfcomposebarview 交付,业务工程 import 即用。
事件等价delegate 四方法 + 两个 NSNotification → 回调 + emitter 事件双通道。
UI 完整Demo 覆盖增高、计数器、按钮、生长方向、禁用态与键盘避让。
仓库规范项目说明、文章、效果图和源码链接统一使用 AtomGit。

本项目的 ArkUI 页面不是对上游 UI 的截图仿制,而是库本体的验收宿主:页面上的每一次高度变化、每一条计数器显隐、每一个按钮状态翻转,都来自移植后的组件代码。

二、实现路线图

第 1 阶段:项目初始化     ── 盘点上游源集、示例边界和 OpenHarmony 交付物
第 2 阶段:工程骨架打通   ── 照已过审 harmonyApp 目录规则搭 DevEco 工程,HAR + entry 双模块
第 3 阶段:规格盘点       ── 抽出常量表、颜色表、行为表与事件表
第 4 阶段:组件移植       ── ComposeBarView / ComposeBarButton 声明式重写(技术难点)
第 5 阶段:事件系统       ── delegate / NSNotification → 回调 + emitter
第 6 阶段:示例与验证     ── Demo、HAP、签名、设备安装、真机运行和效果图

三、逐步实现过程

第 1 阶段:项目初始化

1.1 工程边界
Classes/                        上游 ObjC 源码(三对 .h/.m),原样保留
Example/PHFComposeBarViewExample  上游 iOS 示例工程,原样保留
Example/harmonyApp/             DevEco Stage 工程(本次新增)
  library/                      HAR 库 phfcomposebarview(适配主体)
  entry/                        ArkTS 真机验收页面
README.md / README.upstream.md / README.OpenHarmony.md / README.OpenHarmony_CN.md

鸿蒙工程放在已有 Example/ 目录下(对应 cmp_qrose 审核规则的 example/harmonyApp 位置),不与 iOS 工程互相侵入;Classes/ 不参与鸿蒙构建,仅作为规格书与对照物保留。

1.2 工具链矩阵
项目配置
工程模型hvigor modelVersion 5.1.0,compatibleSdkVersion "5.1.0(18)"
构建工具DevEco Studio 自带 hvigor / ohpm(HarmonyOS SDK 26.0.0.105,API 26)
模块library(HAR)+ entry(HAP)
设备类型phone
Bundleorg.terminator.ohos.phfcomposebarview
版本2.1.0-ohos.1(上游 2.1.0)
1.3 页面边界
标题区          PHFComposeBarView / OpenHarmony demo · 实时栏高
开关区          Grow up / Utility btn / Counter / Enabled
日志区          委托与高度事件输出,最新在顶(对应 iOS Example 的日志 UITextView)
输入栏          工具按钮 + 占位符输入框 + Send 主按钮 + 计数器

页面启动即展示欢迎日志与默认配置的输入栏,全部交互都会在日志区留下一条记录。

第 2 阶段:工程骨架打通

2.1 目录骨架

按已审核通过的 harmonyApp 目录规则创建:

Example/harmonyApp/
├── AppScope/app.json5            bundleName、versionName、图标
├── build-profile.json5           signingConfigs 留空(签名不进仓库)
├── hvigorfile.ts / hvigorconfig.ts / hvigor/hvigor-config.json5
├── oh-package.json5              devDependencies: @ohos/hypium 1.0.21
├── library/                      HAR:harTasks、module.json5(type=har)、index.ets
└── entry/                        HAP:hapTasks、module.json5(type=entry)、EntryAbility + Index
2.2 库与 Demo 的依赖关系
// entry/oh-package.json5
{
  "name": "entry",
  "dependencies": {
    "phfcomposebarview": "file:../library"
  }
}

library 是 HAR 模块(库本体),entry 通过本地 file: 引用消费它——对应上游「CocoaPods 依赖 PHFComposeBarView + Example 演示」的结构。业务工程同样以 HAR 引用集成,发布到 ohpm 后换成版本号即可。

2.3 依赖安装与构建命令
cd Example/harmonyApp
export PATH="/Applications/DevEco-Studio.app/Contents/tools/ohpm/bin:$PATH"
ohpm install --all

export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
export PATH="/Applications/DevEco-Studio.app/Contents/tools/node/bin:/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin:$PATH"
hvigorw --mode module -p product=default -p buildMode=debug assembleHap

产物为 entry/build/default/outputs/default/entry-default-unsigned.hap(debug 约 118 KB)。

第 3 阶段:规格盘点

适配前先把上游源码读成三张表,这一步决定移植件是不是「精确复刻」而不是「看起来像」。

常量表(来自 PHFComposeBarView.m 顶部,1 pt = 1 vp):

上游常量值含义
PHFComposeBarViewInitialHeight44空文本栏高
kHorizontalSpacing8水平间距
kFontSize17输入 / 占位符 / 按钮字号
kTextContainerTop/BottomMargin8 / 8输入容器上下边距
kTextContainerCornerRadius5.25容器圆角
kButtonHeight / kButtonTouchableOverlap26 / 6按钮高 / 标题热区扩展
kUtilityButtonWidth/Height25工具按钮边长
kCharCountFontSize11计数器字号
分隔线高0.5顶部提亮线
kResizeAnimationDuration0.25s程序化增高动画

颜色表:iMessage 蓝 #007AFF(HSB 211,1,1)、禁用灰 #909094、系统灰 #C8C8CC(占位符 / 计数器 / 描边)、近白 #FAFAFA(容器背景 / 提亮线)、工具按钮灰 #8E8E93、毛玻璃用 BlurStyle.Thin 近似 UIToolbar。

行为表:增高公式 clamp(内容高 + 底部增量, 44, maxHeight);行数换算 maxHeight = 44 + (行数-1) × 20;计数器仅在 ≥ 2 行出现、超限不拦截;Send 在文本非空且 enabled 时可用;按压 0.2 立即、恢复 0.2s 动画;高度变化先后广播 Will / Did。

第 4 阶段:组件移植(技术难点)

4.1 三层结构
ComposeBarView.ets      主组件:布局编排、增高、计数器、启用态
ComposeBarButton.ets    按压反馈按钮:文本标题或 SymbolGlyph 两种形态
ComposeBarMetrics/Colors/Events.ets   常量、颜色与事件契约
4.2 增高逻辑:从 sizeThatFits 到自适应约束

上游在 resizeTextViewIfNeededAnimated: 里用 sizeThatFits 算文本高度再 setFrame;声明式写法反过来——TextArea 不设固定高度、自适应内容,用 constraintSize 钳住上限:

TextArea({ text: this.text, placeholder: this.placeholder, controller: this.controller })
  .padding({ left: 6, right: 6, top: 4, bottom: 4 })
  .constraintSize({
    maxHeight: this.effectiveMaxHeight() - 16   // 减去上下边距,整栏不超过 maxHeight
  })

到顶后 TextArea 自动进入内部滚动,恰好等价于上游「到 maxHeight 后 textView 滚动」。整栏用 constraintSize({ minHeight: 44 }) 兜底空态高度。

4.3 单行高度的运行时捕获

上游用空文本 sizeThatFits 测单行高度(计数器「≥ 2 行」的基准)。移植侧在首次布局时从空文本 TextArea 的 onAreaChange 捕获实际值,天然适配系统字体与字体缩放:

private handleTextAreaResize(newHeight: number): void {
  this.textAreaHeight = newHeight;
  if (this.firstLineHeight === 0) {
    this.firstLineHeight = this.text.length === 0 ? newHeight : ComposeBarMetrics.lineHeight + 2;
  }
}
4.4 按压反馈

PHFComposeBarView_Button 的语义是「按下立即降透明度、松开动画恢复」。onTouch 里对 Down 不走动画、对 Up/Cancel 走 200ms animateTo:

if (event.type === TouchType.Down) {
  this.contentOpacity = 0.2;
} else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
  this.getUIContext().animateTo(
    { duration: 200, curve: Curve.EaseOut },
    () => { this.contentOpacity = 1.0; }
  );
}
4.5 API 改名:enabled → inputEnabled

上游 enabled 属性与 ArkUI 通用属性 enabled() 同名,直接声明会与 CustomComponent 基类签名冲突(boolean 无法赋给 (value: boolean) => CommonAttribute),编译器直接拒绝。组件内改名为 inputEnabled,按钮侧对应 pressEnabled,语义不变,README 中给出对照表。

第 5 阶段:事件系统

5.1 两套通知的映射

上游同时提供 PHFComposeBarViewDelegate(四个可选方法)和两个 NSNotification(userInfo 携带 FrameBegin/FrameEnd/Duration/Curve)。移植侧两套都保留:

onButtonPressed: (text: string) => void          // composeBarViewDidPressButton:
onUtilityButtonPressed: () => void               // composeBarViewDidPressUtilityButton:
onWillChangeFrame: (info: ComposeBarFrameChangeInfo) => void
onDidChangeFrame: (info: ComposeBarFrameChangeInfo) => void
onTextChanged: (text: string) => void            // textViewDidChange: 转发
// 对应 NSNotification 的 emitter 事件
emitter.emit({ eventId: ComposeBarNotificationCenter.willChangeFrameEventId }, { data: info });
emitter.emit({ eventId: ComposeBarNotificationCenter.didChangeFrameEventId }, { data: info });
5.2 载荷结构
interface ComposeBarFrameChangeInfo {
  beginHeight: number;   // 变化前栏高(组件按全宽契约,只广播高度)
  endHeight: number;     // 变化后栏高
  duration: number;      // 毫秒;键入驱动的变化为 0(对应上游无动画路径)
  curve: ComposeBarAnimationCurve;  // EaseInOut = 1,即 UIViewAnimationCurveEaseInOut
}

事件在整栏 onAreaChange 里触发,过滤掉宽度变化,只在高度差超过阈值时广播,避免旋转屏幕刷屏。

第 6 阶段:示例与验证

6.1 工程结构
entry/src/main/ets/
├── entryability/EntryAbility.ets    Stage 入口,加载 pages/Index
└── pages/Index.ets                  标题 + 开关 + 日志 + ComposeBarView

Demo 复刻 iOS Example 的委托演示:Send 记录文本并清空收键盘、工具按钮记日志、Will/Did 打印高度变化;另加四个开关即时切换生长方向、工具按钮、计数器与启用态。

6.2 构建、签名和安装
hvigorw --mode module -p product=default -p buildMode=debug assembleHap
# DevEco 自动签名后:
hdc -t <设备序列号> install -r \
  entry/build/default/outputs/default/entry-default-signed.hap
hdc -t <设备序列号> shell aa start \
  -a EntryAbility -b org.terminator.ohos.phfcomposebarview

四、完整代码对照

4.1 整体架构

PHFComposeBarView.m / _Button.m / _TextView.m
    │ 常量 / 颜色 / 行为公式(规格书)
    ▼
ComposeBarMetrics.ets + ComposeBarColors.ets
    ▼
ComposeBarView.ets + ComposeBarButton.ets(HAR phfcomposebarview)
    │ @Prop/@Link + 回调 + emitter
    ▼
Index.ets(Demo)
    │ 开关 + 日志 + 委托接线
    ▼
ArkUI 真机页面

4.2 文件清单

文件职责
library/src/main/ets/common/ComposeBarMetrics.ets全部布局常量、颜色、行数换算公式
library/src/main/ets/common/ComposeBarEvents.ets帧变化契约与 emitter 事件封装
library/src/main/ets/components/ComposeBarView.ets主组件:布局、增高、计数器、启用态
library/src/main/ets/components/ComposeBarButton.ets按压反馈按钮(文本 / 符号两形态)
library/index.etsHAR 导出面
entry/src/main/ets/pages/Index.ets真机验收页面
README.OpenHarmony.md / README.OpenHarmony_CN.md英文摘要 / 中文详述
README.upstream.md / README.md上游文档存档 / 鸿蒙版首页

4.3 关键 API 对照

层次上游(ObjC)移植(ArkTS)
文本text 属性 / setText:animated:@Link text 双向绑定
上限maxLinesCount / maxHeight同名 @Prop,公式一致
计数器maxCharCount同名 @Prop,显隐规则一致
启用enabledinputEnabled(避免通用属性冲突)
按钮buttonTitle / buttonTintColor同名 @Prop
工具钮utilityButtonImageutilityButtonSymbol(SymbolGlyph)
委托composeBarViewDidPressButton:onButtonPressed 回调
委托willChangeFromFrame:toFrame:…onWillChangeFrame 回调
通知Will/DidChangeFrameNotificationemitter 事件(eventId 常量)
常量PHFComposeBarViewInitialHeightComposeBarMetrics.initialHeight

4.4 ArkTS 与 ObjC 的边界

// 上游:命令式,手动测量 + setFrame + 动画块
textHeight = ceilf([_textView sizeThatFits:size].height);
newHeight = MAX(MIN(textHeight + delta, maxHeight), 44);
[UIView animateWithDuration:0.25 animations:^{ self.frame = frame; }];
// 移植:声明式,约束驱动 + 状态广播
.constraintSize({ maxHeight: this.effectiveMaxHeight() - 16 })
.onAreaChange((oldArea: Area, newArea: Area) => {
  this.handleBarResize(Number(oldArea.height), Number(newArea.height));
});

两者之间只传递「高度差」这一事实:声明式框架负责把它变成布局,组件负责把它变成事件。

五、关键决策说明

决策 1:UI 库按 UI 库的方式适配——ArkTS 全量重写

qrose / voyager 等 KMP 库保留 commonMain、补 ohosArm64、走 C ABI + NAPI;PHFComposeBarView 没有 Kotlin 资产,它的价值就是布局与交互规格本身。ObjC 无法编译到 OHOS,跨语言桥接 UIKit 视图树不可行,套 KMP 壳只为一个 UI 组件引入整条 Kotlin 工具链收益为零。把上游源码当规格书读、用 ArkUI 原生表达力重写,是最短且最可维护的路径。

决策 2:度量一比一搬运,运行时量动态捕获

静态常量(44 / 8 / 25 / 5.25 / 0.5 / 0.25s / 0.2 / 0.2s)直接进 ComposeBarMetrics;上游靠 sizeThatFits 测的两个运行时量(单行高度、行高增量)分别用首次布局捕获和 20 常量换算承接,公式 maxHeight = 44 + (行数-1) × 20 与上游逐字一致。

决策 3:HAR 库 + entry 双模块交付

库本体是 HAR phfcomposebarview,Demo 通过 file:../library 消费。业务工程集成路径与 Demo 完全一致,未来发布 ohpm 只改一行依赖。这对应上游「pod 依赖 + Example」的结构。

决策 4:事件系统双通道

保留 delegate 风格回调(常用方)和通知风格 emitter 事件(解耦方),载荷结构对齐上游 userInfo 键。组件按全宽契约只广播高度维度,使用方读 endHeight 即可完成布局避让。

决策 5:ArkUI 页面作为验收宿主

页面上的日志、高度数值、开关状态全部来自真实组件回调——不复制业务判断,不摆静态文字。验收看到什么,组件就真的做了什么。

六、测试与验证

6.1 测试环境

本次验证使用 macOS ARM64、DevEco Studio 5.1.0 Release(HarmonyOS SDK 26.0.0.105 / API 26 工具链)、DevEco 自带 hvigor / ohpm,并使用华为鸿蒙真机完成签名安装与运行验收。

6.2 构建验证

:entry:default@CompileArkTS: finished
:entry:default@PackageHap: finished
:entry:default@SignHap: skipped(签名按规范留空,本地签名)
BUILD SUCCESSFUL in 2s

6.3 HAP 验证

install bundle successfully
start ability successfully

签名后的 HAP 在真机安装并启动,页面进入前台,输入栏、开关与日志区全部渲染正常。

6.4 功能用例

  1. 启动即见 44vp 左右的输入栏、占位符 “Type something…”、禁用态 Send 与左侧相机工具按钮;
  2. 输入单行文本,Send 变蓝可点;
  3. 输入至换行,栏瞬时增高,日志出现 “Height changing by N” / “Height changed”,标题栏高度同步;
  4. 第二行起计数器 n/160 出现在 Send 上方;
  5. 超过 160 字显示如 170/160,颜色不变、输入不拦截;
  6. 超过 5 行后栏高停在 124vp,输入框内部滚动;
  7. 点 Send 记录 Main button pressed. Text:… 并清空、收起键盘;
  8. 点相机记录 Utility button pressed;
  9. 关闭 “Grow up”,输入栏移到顶部、向下生长;
  10. 关闭 “Utility btn”,输入区回收 33vp;关闭 “Counter”,计数器消失;
  11. 关闭 “Enabled”,输入框与两按钮全部禁用;
  12. 按压按钮内容立即降至 0.2 透明度,松开 0.2s 恢复;
  13. 键盘弹出时系统键盘避让自动抬升输入栏。

6.5 验证结论

HAR 库、ArkUI 页面和签名 HAP 已完成链路验证。应用在真机上成功安装并进入前台,页面展示了真实的输入栏行为:高度事件、计数器显隐、按钮状态翻转与按压反馈均来自移植后的组件代码。

七、运行效果

7.1 真机截图

在这里插入图片描述

7.2 命令速查

git clone https://atomgit.com/oh-tpc/PHFComposeBarView.git
cd PHFComposeBarView/Example/harmonyApp
export PATH="/Applications/DevEco-Studio.app/Contents/tools/ohpm/bin:$PATH"
ohpm install --all
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
export PATH="/Applications/DevEco-Studio.app/Contents/tools/node/bin:/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin:$PATH"
hvigorw --mode module -p product=default -p buildMode=debug assembleHap
# 签名后安装:
hdc -t <设备序列号> install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc -t <设备序列号> shell aa start -a EntryAbility -b org.terminator.ohos.phfcomposebarview

八、遗留问题与改进方向

8.1 踩坑复盘

  1. @Prop enabled 与通用属性 enabled() 签名冲突,编译直接失败,改名 inputEnabled / pressEnabled 解决——iOS 属性名直译前要先对照 ArkUI 通用属性表;
  2. SymbolGlyph.fontColor 只收 ResourceColor[],传字符串报 overload 错误,需包一层数组;
  3. TextAreaController 没有 setText,外部清空走 TextArea({ text }) 状态绑定(官方支持 $ / !! 双向绑定);
  4. file:../library 依赖要显式 ohpm install --all,hvigorw --sync 不会兜底,否则报 Cannot find module;
  5. 命令行工具版 hvigor 对 5.1.0(18) 解析报 “SDK component missing”,换 DevEco 自带 hvigor + DEVECO_SDK_HOME 指向 DevEco SDK 后通过;
  6. 父组件初始化 @Link 必须 $var,传 this.var 报 UI 组件语法错误;
  7. TextArea 默认内边距会撑高首行,必须显式设置 padding,否则单行栏高超出 44 契约。

8.2 已知问题

  • 首次布局会广播一次「从 0 到初始高度」的事件(Demo 日志可见),上游无此初始通知;如需对齐可在事件入口过滤 beginHeight === 0;
  • 单行实际栏高约 45vp(TextArea 实际行高略大于 20),与 44 契约相差 1vp 左右;
  • 占位符自动缩字(上游 minimumScaleFactor 0.824)未实现,超长占位符截断;
  • 毛玻璃为 BlurStyle.Thin 近似,UIToolbar 材质无法逐像素复刻;
  • 高度变化的 Will/Did 在布局后同一时机先后发出,无「动画前」钩子;
  • 深色模式仅背景模糊跟随系统,容器与文字色沿用上游浅色规格。

8.3 未来优化方向

  • 过滤初始布局事件,补宽度变化事件开关;
  • 为 ComposeBarColors 提供深色色板与主题注入;
  • 组件级 onDidChangeFrame 与业务 inset 联动的官方样例(聊天列表避让);
  • 发布 phfcomposebarview 到 ohpm 中央仓,业务工程按版本号引用;
  • 补充 HiLog 单元测试与 DevEco Testing UI 自动化用例。

九、总结

9.1 核心难点回顾

UIKit 命令式布局规格
    → 常量 / 颜色 / 公式盘点(pt == vp)
    → 声明式 ArkTS 重写(约束驱动增高)
    → 运行时度量捕获(单行高度)
    → 事件系统映射(delegate / NSNotification → 回调 + emitter)
    → ArkUI 真机页面 + 签名 HAP 验收

9.2 封装层次

  • 库层:phfcomposebarview HAR,承载全部布局与交互规格;
  • 组件层:ComposeBarView(编排)+ ComposeBarButton(按压反馈)+ 契约常量;
  • 事件层:回调与 emitter 双通道,载荷对齐上游 userInfo;
  • 页面层:开关、日志与实时栏高构成的可验收宿主;
  • 工程层:hvigor 双模块构建、ohpm 本地依赖、签名与 hdc 安装。

9.3 三条经验

  1. UI 库的适配价值在规格对齐,不在代码转译——先读出常量表、行为表、事件表,再动手写声明式实现;
  2. 命令式转声明式时,把「测量 + setFrame」换成「约束 + onAreaChange」,把「动画块」换成「animateTo 包裹状态更新」,公式与数值保持不动;
  3. API 命名冲突(如 enabled)要在组件设计期对照通用属性表排雷,避免返工编译错误。

9.4 适配成果

当前适配已完成:

  • 上游 2.1.0 全部公开能力(增高、上限、计数器、按钮、工具钮、事件、禁用态)的 ArkTS/ArkUI 移植;
  • HAR 库 phfcomposebarview 2.1.0-ohos.1 与 file: 依赖的消费链路;
  • DevEco Stage 真机验收页面(四开关 + 事件日志 + 实时栏高);
  • 无签名构建、本地签名、真机安装与运行验证;
  • AtomGit 项目文档(首页 / 上游存档 / 英文摘要 / 中文详述)与本文效果图。

适配过程 FAQ

以下问题来自 ohosArm64 目标接入、NAPI 桥接、HAP 构建与真机验收的实际流程,按出现频率排序。

Q1:DevEco Studio Sync 失败或 ohpm 依赖下载超时,怎么处理?

  • 确认 DevEco Studio 版本与工程 build-profile.json5 的 compatibleSdkVersion 一致;
  • 在 File > Settings > Build, Execution, Deployment > Ohpm 配置可用的 ohpm 仓库镜像或代理;
  • 删除 oh_modules、oh-package-lock.json5 与 .hvigor 缓存后重新 Sync;
  • 公司网络需要代理时,同时给 ohpm 与 hvigor 配置 http.proxy。

Q2:CMake 报 Missing .../libxxx.so 或找不到 Kotlin/Native 动态库?

  • 先在 example 目录执行 ./gradlew :nativeApp:linkDebugSharedOhosArm64(模拟器用 linkDebugSharedOhosX64);
  • 确认 libxxx.so 与 libc++_shared.so 已复制到 ohos/entry/libs/<abi>/;
  • 检查 entry/build-profile.json5 的 abiFilters 与设备 ABI 是否一致(真机 arm64-v8a,模拟器 x86_64);
  • 清理 entry/.cxx 与 entry/build 后重试。

Q3:ArkTS 报 Cannot find module 'libxxx_napi.so' 或调用返回 undefined?

  • 确认 CMake 的 add_library(<target> SHARED ...) 名称与 import ... from 'libxxx_napi.so' 完全一致;
  • 解包 HAP,确认 libs/<abi>/libxxx_napi.so 已打包;
  • 删除 entry/build、entry/.cxx 与 oh_modules 后重新构建;
  • 检查 module.json5 的 srcEntry 与页面路径是否正确。

Q4:真机安装报 signature verification failed 或 install failed?

  • 在 DevEco Studio 的 Signing Configs 配置证书、Profile 与密码;
  • bundleName 必须与签名 profile 中的 bundle name 一致;
  • 设备需加入 profile 的 deviceIds,调试证书有效期需覆盖当前时间;
  • 用 hdc list targets 确认设备在线,再执行 hdc install -r xxx-signed.hap。

Q5:应用启动白屏、闪退或 NAPI 调用崩溃?

  • 用 hdc hilog | grep -i "napi\|kotlin" 查看原生日志;
  • 确认 libc++_shared.so 与 Kotlin/Native 工具链版本匹配,且 ABI 与设备一致;
  • 检查 C ABI 返回的字符串是否由 Native 侧分配、ArkTS 侧复制后是否调用 XxxFree 释放;
  • 在 DevEco 的 Native 调试配置中附加 lldb,定位空指针与越界访问。

Q6:Gradle 报 ohosArm64 目标不存在或 Kotlin/Native 编译失败?

  • 使用鸿蒙发行版 Kotlin(如 2.2.21-1.0.0),并在 settings.gradle.kts 配置 eazytec 仓库;
  • Gradle 建议 8.x、JDK 21,版本不匹配时先统一工具链;
  • 确认 kotlin { ohosArm64() } 已应用,且 local.properties 的 SDK 路径有效;
  • 清理 ~/.gradle/caches 中对应版本的 Kotlin/Native 缓存后重试。

Q7:HAP 构建报 SDK 版本不匹配或 compatibleSdkVersion 错误?

  • 对齐 DevEco Studio、HarmonyOS SDK 与 build-profile.json5 中的 compatibleSdkVersion;
  • 升级 DevEco Studio 后同步升级 hvigor 与 SDK,避免 modelVersion 与 IDE 不匹配;
  • 命令行构建时设置 DEVECO_SDK_HOME 指向 DevEco 的 sdk 目录。

Q8:命令行 hvigor 构建失败,但 DevEco 里能构建?

  • 使用 DevEco 自带的 node 与 hvigor:
    "$DEVECO/Contents/tools/node/bin/node" "$DEVECO/Contents/tools/hvigor/bin/hvigorw.js" ...;
  • 设置 JAVA_HOME、NODE_HOME、DEVECO_SDK_HOME;
  • 加上 --no-daemon 排除守护进程缓存影响,并清理 entry/build 与 .hvigor。

参考文档

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐