开源鸿蒙平台CMP 三方库「PHFComposeBarView」适配全流程
本文记录
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 / buttonTintColor | Send 按钮标题与着色。 |
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 |
| Bundle | org.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):
| 上游常量 | 值 | 含义 |
|---|---|---|
PHFComposeBarViewInitialHeight | 44 | 空文本栏高 |
kHorizontalSpacing | 8 | 水平间距 |
kFontSize | 17 | 输入 / 占位符 / 按钮字号 |
kTextContainerTop/BottomMargin | 8 / 8 | 输入容器上下边距 |
kTextContainerCornerRadius | 5.25 | 容器圆角 |
kButtonHeight / kButtonTouchableOverlap | 26 / 6 | 按钮高 / 标题热区扩展 |
kUtilityButtonWidth/Height | 25 | 工具按钮边长 |
kCharCountFontSize | 11 | 计数器字号 |
| 分隔线高 | 0.5 | 顶部提亮线 |
kResizeAnimationDuration | 0.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.ets | HAR 导出面 |
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,显隐规则一致 |
| 启用 | enabled | inputEnabled(避免通用属性冲突) |
| 按钮 | buttonTitle / buttonTintColor | 同名 @Prop |
| 工具钮 | utilityButtonImage | utilityButtonSymbol(SymbolGlyph) |
| 委托 | composeBarViewDidPressButton: | onButtonPressed 回调 |
| 委托 | willChangeFromFrame:toFrame:… | onWillChangeFrame 回调 |
| 通知 | Will/DidChangeFrameNotification | emitter 事件(eventId 常量) |
| 常量 | PHFComposeBarViewInitialHeight | ComposeBarMetrics.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 功能用例
- 启动即见 44vp 左右的输入栏、占位符 “Type something…”、禁用态 Send 与左侧相机工具按钮;
- 输入单行文本,Send 变蓝可点;
- 输入至换行,栏瞬时增高,日志出现 “Height changing by N” / “Height changed”,标题栏高度同步;
- 第二行起计数器
n/160出现在 Send 上方; - 超过 160 字显示如
170/160,颜色不变、输入不拦截; - 超过 5 行后栏高停在 124vp,输入框内部滚动;
- 点 Send 记录
Main button pressed. Text:…并清空、收起键盘; - 点相机记录
Utility button pressed; - 关闭 “Grow up”,输入栏移到顶部、向下生长;
- 关闭 “Utility btn”,输入区回收 33vp;关闭 “Counter”,计数器消失;
- 关闭 “Enabled”,输入框与两按钮全部禁用;
- 按压按钮内容立即降至 0.2 透明度,松开 0.2s 恢复;
- 键盘弹出时系统键盘避让自动抬升输入栏。
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 踩坑复盘
@Prop enabled与通用属性enabled()签名冲突,编译直接失败,改名inputEnabled/pressEnabled解决——iOS 属性名直译前要先对照 ArkUI 通用属性表;SymbolGlyph.fontColor只收ResourceColor[],传字符串报 overload 错误,需包一层数组;TextAreaController没有setText,外部清空走TextArea({ text })状态绑定(官方支持$/!!双向绑定);file:../library依赖要显式ohpm install --all,hvigorw --sync不会兜底,否则报Cannot find module;- 命令行工具版 hvigor 对
5.1.0(18)解析报 “SDK component missing”,换 DevEco 自带 hvigor +DEVECO_SDK_HOME指向 DevEco SDK 后通过; - 父组件初始化
@Link必须$var,传this.var报 UI 组件语法错误; - 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 封装层次
- 库层:
phfcomposebarviewHAR,承载全部布局与交互规格; - 组件层:
ComposeBarView(编排)+ComposeBarButton(按压反馈)+ 契约常量; - 事件层:回调与 emitter 双通道,载荷对齐上游 userInfo;
- 页面层:开关、日志与实时栏高构成的可验收宿主;
- 工程层:hvigor 双模块构建、ohpm 本地依赖、签名与 hdc 安装。
9.3 三条经验
- UI 库的适配价值在规格对齐,不在代码转译——先读出常量表、行为表、事件表,再动手写声明式实现;
- 命令式转声明式时,把「测量 + setFrame」换成「约束 + onAreaChange」,把「动画块」换成「animateTo 包裹状态更新」,公式与数值保持不动;
- 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。
参考文档
更多推荐


所有评论(0)