本文记录把 react-native-floating-action(悬浮操作按钮 / FAB)适配到 HarmonyOS 的完整过程。

它做的事很专一:在屏幕一角放一个圆形主按钮,点开之后绕着它展开一排动作按钮(可带文字标签),点某个动作就把 name 回调出去。

结论分两半:

  • ✅ 工程质量很高,主体可用 —— 纯 JS、零外部依赖、老写法九项预扫全空、六处平台分支全是"iOS 特判 + 优雅降级"、13 处 useNativeDriver 全部显式 false;19 / 19 条判据全部通过,几何能做到像素级精确核对。
  • ⚠️ 但"点开动作列表"这一步在 RNOH 上不可见 —— 主按钮回调会触发、内部 state.active 也确实变成了 true、actions 有 3 项、LayoutAnimation 可用,但屏上既看不到动作按钮、也看不到动作文字。现象已钉死,根因定位到渲染层(详见第四节,明确标注"待进一步定位")。

★★★★ 一、像素级几何验证:三组预言全部命中

defaultProps(源码 L620-639)给了很明确的默认值:

buttonSize: 56,        // 主按钮边长
distanceToEdge: 30,    // 距屏幕边缘
position: "right",     // 默认贴右
iconWidth: 15, iconHeight: 15,   // 加号图标的尺寸
color: "#1253bc",

而加号本身是两个 View 拼的(src/AddIcon.js):

<View style={[styles.vertical,   { height, backgroundColor }]} />   // vertical: { width: 2, position: "absolute" }
<View style={[styles.horizontal, { width,  backgroundColor }]} />   // horizontal: { height: 2, position: "absolute" }

density 3 ⇒ 预言与实测对照:

量公式预言实测(布局 dump)
主按钮边长buttonSize 56vp × 3168px✅ 168×168(left 实例)/ 169×169(right 实例)
距下边缘distanceToEdge 30vp × 390px✅ 90px(两个实例都是)
距左边缘(position: "left")30vp × 390px✅ 90px(x=[90,258])
距右边缘(position: "right")30vp × 390px✅ 90px(x=[1061,1230],1320−1230=90)
加号竖条宽2vp × 36px✅ 6px(x=[1143,1149])
加号竖条长iconHeight 15vp × 345px✅ 45px(y=[2036,2081])
加号横条高2vp × 36px✅ 6px(y=[2055,2061])
加号横条宽iconWidth 15vp × 345px✅ 45px(x=[1124,1169])

原始 dump 片段:

Custom#View [1061,1973,1230,2142]  clickable      ← F1(right)169×169,距右 90px、距下 90px
Custom#View [ 90,1974, 258,2142]  clickable      ← F2(left) 168×168,距左 90px、距下 90px
Custom#View [1143,2036,1149,2081]                ← 加号竖条:宽 6px、长 45px
Custom#View [1124,2055,1169,2061]                ← 加号横条:宽 45px、高 6px

⇒ 56vp→168px、30vp→90px、2vp→6px、15vp→45px 四组换算全部精确命中 ✓✓

一个细节(如实记录):两个实例的实测边长是 168 与 169 px(理论值 168),存在 1px 取整差 ——
position: "left" 用左偏移定位、position: "right" 用右偏移定位,两种定位方式的像素取整结果不同 ✓
这个差异也让我的第一次测量漏掉了 F1(见第六节)。

★★★★ 二、visible={false} ⇒ 整棵子树返回 null

render() 的第一句(源码 L543-549):

render() {
  const { active, shouldRender } = this.state;
  const { showBackground, visible } = this.props;
  if (!shouldRender && !visible) {
    return null;                     // ★ 从未挂载过 + visible=false ⇒ 什么都不渲染
  }
  return (<Animated.View pointerEvents="box-none" style={[styles.overlay, …]}>…</Animated.View>);
}

实测(布局 dump):

阶段visible屏上的 168±1px 方块
P0false只有 F2(left) —— F1 完全不存在 ✓
P1trueF1 + F2 各一组 ✓
P2falseF1 再次消失 ✓

这一点在截图上直接可见(下图就是 P2 时刻:右下角的蓝色 F1 消失,左下角的绿色 F2 仍在):

在这里插入图片描述

⇒ visible=false 会让整个组件(含遮罩层)从树上消失 ⇒ 这是一个很干净的开关语义 ✓
⇒ 实践含义:如果你希望"隐藏但保留状态",不要用 visible={false}(state.active 虽然保留、但树被拿掉,回到 true 时会重新走 componentDidMount 之外的分支);对"动画收起"应该用组件自己的开合逻辑。

★★★★ 三、shadeColor 纯函数:四条断言全部精确命中

src/utils/color.js 里有个纯函数(被 getRippleProps 用于生成涟漪色):

export function shadeColor(color, percent) {
  // 支持 "#RRGGBB" 与 "rgb(...)"/"rgba(...)" 两种输入
  R = parseInt((R * (100 + percent)) / 100, 0);
  …
  R = R < 255 ? R : 255;        // ★ 上限 clamp 到 255(只 clamp 上限)
  …
  return `#${RR}${GG}${BB}`;    // ★ 每通道补零到两位
}

交付包没有 exports 字段 ⇒ 可以用子路径 require 直接单测:

const {shadeColor} = require('react-native-floating-action/src/utils/color');

实测:

输入手算实测
shadeColor('#FF0000', -30)R=⌊255×0.7⌋=178=0xB2, G=0, B=0✅ #b20000
shadeColor('#1253bc', 0)不变✅ #1253bc
shadeColor('rgb(255,0,0)', 50)R=382 ⇒ clamp 到 255✅ #ff0000
shadeColor('#80FF80', -50)R=64=0x40, G=⌊255×0.5⌋=127=0x7f, B=64=0x40✅ #407f40

⇒ 四条覆盖了十六进制输入、rgb() 输入、上限 clamp、各通道独立计算 + 补零 ✓✓
⇒ 第四条尤其有价值:它同时验证了"三通道各自计算"与"两位数补零"(0x40 需要补成 40,而 0x7f 是小写 ✓)

★★★★ 四、核心问题:点开主按钮后,动作列表在 RNOH 上不可见

4.1 现象(已多次复现)

// FloatingAction.js L235-294 —— 主按钮的 onPress
animateButton = () => {
  …
  if (overrideWithAction) { this.handlePressItem(actions[0].name); return; }   // 我传 false ⇒ 不走这里
  if (onPressMain) { onPressMain(!active); }                 // ① 回调
  if (!active) {
    if (!floatingIcon) { if (animated) { Animated.spring(this.animation, {toValue: 1, useNativeDriver: false}).start(); } }
    if (animated) {
      Animated.spring(this.actionsAnimation, {toValue: 1, useNativeDriver: false}).start();   // ② 动作容器透明度
      LayoutAnimation.configureNext({ duration: 180, create: {…} });                          // ③ 布局动画
    }
    this.updateState({ active: true }, () => { if (onOpen) { onOpen(); } });                  // ④ 置 active
  } else { this.reset(); }
};

逐环节实测(每一步都有证据):

环节期望实测证据来源
① onPressMain 回调被调用✅ 触发页面事件区 + hilog EVENT | onPressMain(F1 主按钮被点)
② Animated.spring(actionsAnimation)值推进到 1⚠️ 未能确认——
③ LayoutAnimation.configureNext可用✅ typeof = object、调用返回成功(无抛错)页面探针 A3
④ state.active变成 true✅ 确实变成 true周期性探针 PROBE | F1.state.active = true / actions = 3
结果动作项出现在屏上❌ 屏上没有动作项布局 dump 里既无 动作一/二/三 文本,也无额外圆按钮

探针原始输出:

===== A3. LayoutAnimation 探针 =====
NOTE | RN.LayoutAnimation 的 typeof = object
NOTE | 调用 LayoutAnimation.configureNext 是否成功 = true
NOTE | configureNext 的错误信息 =
EVENT | onPressMain(F1 主按钮被点)
PROBE | F1.state.active = true / shouldRender = true / props.visible = true / actions = 3

⇒ 回调、状态、动作数量、LayoutAnimation 全部正常,唯独动作项不可见 ✓

4.2 定位(收敛到渲染层)

renderActions()(源码 L453-493):

if (!actions || actions.length === 0) { return undefined; }
if (overrideWithAction) { return null; }
if (animated) {
  animatedActionsStyle = { opacity: this.actionsAnimation.interpolate({ inputRange: [0, 1], outputRange: [0, 1] }) };
}
const actionsStyles = [ styles.actions, styles[`${position}Actions`], animatedActionsStyle, { bottom: this.actionsBottomAnimation } ];
// styles.actions      = { position: "absolute", bottom: 85, zIndex: 10 }
// styles.rightActions = { alignItems: "flex-end", right: -1000 }   ← 收起:移出屏外
// (展开态另有 rightActionsVisible = { right: 0 })

⇒ 动作容器的可见性由 opacity: this.actionsAnimation(Animated.spring 驱动)决定,
并且它还被 { bottom: this.actionsBottomAnimation } 与 right: -1000 / 0 的动画值控制 ✓

在"回调已触发、active 已为 true、LayoutAnimation 无异常"的前提下,最可能的原因是: Animated.spring驱动的this.actionsAnimation/this.actionsBottomAnimation**没有推进**, 导致动作容器停在opacity: 0(bottom/right` 也停在收起值)。

★★★ 4.3 一条重要的方法论:布局 dump【不列出 opacity: 0 的节点】

这解释了为什么"dump 里找不到动作项"不能直接推断"没有渲染":

  • 动作容器很可能已经渲染,只是 opacity 停在 0;
  • 而布局 dump 只列出参与布局/可见的节点 ⇒ opacity: 0 的子节点不会出现 ✓

⇒ 所以判定"某节点是否渲染"必须用 JS 侧证据(实例 state、日志),不能只看 dump ✓

⚠️ 本节结论的边界:Animated 值是否推进本轮没有直接读到(读实例上的 Animated.Value 需要再加一次探针与重建)。
因此这一节写的是"现象已钉死 + 根因收敛到渲染层的 Animated 驱动",不是"根因已确认"。

★★★ 五、平台分支与 useNativeDriver 的完整清单(都很好)

六处 Platform.OS,全是"iOS 特判 + 优雅降级":

位置代码鸿蒙上的行为
utils/platform.js L7isIphoneX():Platform.OS === "ios" && !Platform.isPad && …返回 false ✓
utils/touchable.js L10useNativeFeedback && Platform.OS === "android" ⇒ 返回 TouchableNativeFeedback返回 TouchableOpacity ✓(正确降级)
utils/touchable.js L20涟漪:同上 + Platform.Version >= 21返回 {}(无涟漪)✓
FloatingAction.js L69键盘事件名:ios ? "keyboardWillShow" : "keyboardDidShow"用 keyboardDidShow ✓
FloatingAction.js L71同上(hide)用 keyboardDidHide ✓
FloatingAction.js L677overflow: Platform.OS === "ios" ? "visible" : "hidden"hidden ✓

⇒ 六处没有一处会崩,而且**"非 iOS/非 Android"都落到了合理的默认分支** ✓

十三处 useNativeDriver 全部显式 false:

FloatingAction.js L90/L91/L96/L97/L140/L146/L159/L165/L220/L221/L264/L269
FloatingActionItem.js L19

⇒ 全部显式传值 ⇒ 不会触发"未指定 useNativeDriver"的告警 ✓
⇒ 也说明作者对动画属性(bottom / right / opacity 等布局属性)不能用原生驱动这件事是清楚的 ✓

★★★ 六、我自己的两次测量失误(如实记录)

  1. 过滤器太严,把 F1 漏掉了 ✗:我用 宽 == 168 且 高 == 168 精确匹配去找主按钮,而 position="right" 的实例实测是 169×169(另见第一节的 1px 取整差)⇒ 它一直没被匹配到,我一度以为"右侧的 FAB 没渲染"。
    ⇒ 教训:几何过滤要给容差(如 ±8px),并把"同一元素的多层嵌套节点"去重后再下结论。
  2. dump 时机落在"已收起"的相位 ✗:第一次点完主按钮后我等了 3 秒才 dump,而那一刻时间轴刚好把 visible 置回 false ⇒ render 返回 null ⇒ 整棵树都没了,我却以为是"展开失败"。
    ⇒ 教训:驱动型测试页必须让"被测状态"稳定存在足够久(本次改为手动按钮 + 1.5 秒内立刻 dump 才定案)。

改了这两点之后,才把"动作项不可见"从"疑似"钉成"可复现现象" ✓

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


七、接入与预检

步做法结果
①harmony.autolinking没有
②harmony/ / ios/ / android/都没有
③原生痕迹预扫✅ 无原生源文件、无 NativeModules
④★ 老写法九项预扫✅ 全部为空(见下)
⑤平台分支预扫✅ 6 处,全是 iOS 特判 + 优雅降级
⑥measure() / onLayout 排查✅ 都没用到
⑦scripts 扫 prepare 家族✅ 无
⑧依赖✅✅ dependencies 为空;import 图只有 react / react-native / prop-types(宿主已有)⇒ 零外部依赖
⑨写法两个 class(AddIcon extends PureComponent、FloatingAction extends Component);原型方法 + 箭头类字段混用

老写法九项逐项结果:

项结果
React.createClass✅ 空
React.PropTypes✅ 空
从 react 具名导入 PropTypes✅ 空
ViewPropTypes / TextPropTypes✅ 空
字符串 ref="x" / this.refs.x✅ 空
mixins:✅ 空
componentWillReceiveProps✅ 空
componentWillMount✅ 空
measure( / measureInWindow✅ 空

两类成员混用(实测):

NOTE | 原型方法命中数(源码 5 个) = 5
NOTE | 5 个箭头类字段里在 prototype 上的个数(应为 0) = 0
PASS | ★ 5 个原型方法都在 prototype 上            | 期望=5 | 实际=5
PASS | ★★ 5 个箭头类字段都不在 prototype 上       | 期望=0 | 实际=0

(原型:constructor / componentDidMount / componentDidUpdate / componentWillUnmount / render
箭头类字段:getIcon / reset / animateButton / getShadow / onKeyboardHideHide / …)

⇒ 用的是 componentDidUpdate(现代生命周期),不是 componentWillReceiveProps ✓

# 从适配仓库安装(零外部依赖,无需补装任何运行时依赖)
npm install "git+https://atomgit.com/oh-react-native/react-native-floating-action.git#1.22.0-ohos-1.0.0" --ignore-scripts

八、交付包质量与基线说明

类别结果
交付包内容index.js(67 B,只 re-export)+ src/ 7 个文件(主组件 17562 B)✓
spec.json⚠️ 缺 upstreamCommit
validation⚠️ "build-pass-device-pending"
⚠️ 声明的基线reactNative: "0.88.0" + rnoh: "0.84.4"(verification: evidence/batch50/…)⇒ 两字段自相矛盾(rnoh 0.84.4 是配 RN 0.84.x 的),且既不是当前宿主(0.84.1/0.84.3)也不是权威 0.88 基线

关于基线:本次验证在 RN 0.84.1 + RNOH 0.84.3 上完成。这个库没有任何已移除的写法;
不过第四节那个"动作列表不可见"的问题与 Animated 的执行有关,建议在换基线时复验一次。


九、已知限制与本次验证边界

9.1 用这个库要知道的

事项说明
⚠️ 点开动作列表在 RNOH 上不可见回调会触发、state.active 会变 true,但动作项不可见(详见第四节;根因收敛到渲染层的 Animated 驱动,未最终确认)
★ visible={false} 会让整棵树返回 null是"开关"语义,不适合做"动画收起"
★★ 几何可精确预言主按钮 56vp ⇒ 168px;distanceToEdge 30vp ⇒ 90px;加号线宽 2vp ⇒ 6px、图标 15vp ⇒ 45px
★ position="right" 与 "left" 有 1px 取整差实测 169px vs 168px(理论 168)⇒ 写断言要给容差
★ 六处平台分支都是优雅降级鸿蒙上:TouchableOpacity、无涟漪、keyboardDidShow/Hide、overflow: hidden
★ 13 处 useNativeDriver 全为 false不会触发"未指定"告警
★ 纯函数 shadeColor 可子路径单测require('react-native-floating-action/src/utils/color')
★ 只 clamp 上限shadeColor 的 R < 255 ? R : 255 —— 负值不会 clamp 到 0(做深色时要注意)

9.2 本次验证的边界

  1. ★★ 动作列表不可见的根因未最终确认(Animated 值是否推进没有直接读到;需要再读实例上的 Animated.Value);
  2. 未验证 onPressItem 的回调链路(动作项没渲染出来,也就点不到);
  3. 未验证 onOpen / onClose / onStateChange / onPressBackdrop 回调;
  4. 未验证 overrideWithAction(主按钮直接执行第一个动作);
  5. 未验证 floatingIcon(自定义主图标)与自定义 icon;
  6. 未验证 showBackground 的遮罩层与 overlayColor;
  7. 未验证 listenKeyboard / dismissKeyboardOnPress 的键盘联动;
  8. 未验证 animated={false} 的非动画路径(源码里这条分支与动画分支不同);
  9. 未验证 position="center"(该分支用到 DEVICE_WIDTH / 2 - 28);
  10. 未验证 distanceToEdge 传对象({vertical, horizontal})的形式;
  11. 未在 RN 0.88.0 / RNOH 0.84.4 上复验(交付包声明的基线);
  12. 未测真机与其它屏幕宽度(只验证了 440vp 宽);
  13. 未做跨平台对照(未在 Android/iOS 上跑同一页做对比)。

未改动库代码。


十、常见问题

Q1:它在 RN 0.84 上能用吗?

主体能用:组件正常渲染、visible 开合与 position 定位都正确、几何尺寸像素级准确、纯函数与契约全部通过(19/19)。
但"点开动作列表"这一步目前看不到动作项(详见第四节)。

Q2:为什么我点了主按钮没反应?

先分清"哪种没反应":

  • onPressMain 不触发 ⇒ 检查 overrideWithAction(为 true 时它会先执行第一个动作然后 return,不会再调 onPressMain);
  • 回调触发了但没有动作项 ⇒ 就是本文第四节的现象(回调、active、LayoutAnimation 都正常,但动作容器不可见)。

Q3:visible={false} 和"收起"有什么区别?

visible={false} 且从未挂载过时,render() 直接 return null ⇒ 整棵子树不存在(含遮罩)。
“展开/收起"是组件内部 state.active 的事,与 visible 无关。若要做"动画收起”,用组件自身的开合逻辑,别用 visible。

Q4:我想改主按钮大小和离边距离?

<FloatingAction buttonSize={56} distanceToEdge={30} position="right" />

实测对应关系(density 3):buttonSize × 3 = 像素边长;distanceToEdge × 3 = 距边像素。

Q5:shadeColor 怎么用?

const {shadeColor} = require('react-native-floating-action/src/utils/color');
shadeColor('#FF0000', -30);        // '#b20000'
shadeColor('#1253bc', 0);          // '#1253bc'
shadeColor('rgb(255,0,0)', 50);    // '#ff0000'(超上限 clamp)
shadeColor('#80FF80', -50);        // '#407f40'

⚠️ 只 clamp 上限,负值不会 clamp 到 0。

Q6:怎么在不点屏幕的情况下测它?

它是个 class,但交互方法都是箭头类字段(挂在实例上) ⇒ 用 ref 直接调:

const ref = useRef(null);
<FloatingAction ref={ref} visible={show} actions={[…]}
  onPressMain={v => console.log('open?', v)} />

ref.current.animateButton();                 // 等价于点主按钮
ref.current.state.active;                    // 读内部展开状态

⚠️ 但要注意 visible 一旦为 false 且未挂载过,实例上的 state 仍在但树被拿掉 —— 判据要读状态,不要只看布局 dump。

Q7:为什么布局 dump 里找不到某个节点?

布局 dump 不列 opacity: 0 的节点 ⇒ “dump 里没有” 不等于 “没渲染”。
判断是否渲染要用 JS 侧证据(实例 state、日志),或把 opacity 临时改成 1 再看。


小结

react-native-floating-action 的工程质量很高:纯 JS、零外部依赖、老写法九项预扫全空、
六处平台分支全是"iOS 特判 + 优雅降级"、13 处 useNativeDriver 全部显式 false。
几何能做到像素级核对(19/19 判据通过)。

最值得记下来的是这几条:

  1. ★★★★ 像素级几何验证:四组换算全部精确命中。
    buttonSize 56vp → 168px、distanceToEdge 30vp → 90px、加号线宽 2vp → 6px、图标 15vp → 45px ✓
    两个实例分别在左下与右下,距左/右与距下都是 90px ✓

  2. ★★★★ 一个"现象已钉死、根因已收敛"的适配问题。
    点主按钮后:onPressMain 回调触发 ✓、state.active 变 true ✓、actions = 3 ✓、LayoutAnimation.configureNext 可用且成功 ✓ —— 但动作项在屏上不可见 ❌
    源码显示动作容器的可见性由 opacity: this.actionsAnimation.interpolate(...) 与 {bottom: this.actionsBottomAnimation} 两个 Animated 值决定
    ⇒ 根因收敛到"Animated.spring 驱动的值未推进",未最终确认(如实标注)。

  3. ★★★ 布局 dump 不列 opacity: 0 的节点 —— 所以"dump 里没有"≠"没渲染"。
    判定是否渲染必须用 JS 侧证据(实例 state / 日志),或临时把 opacity 改成 1。

  4. ★★★★ visible={false} ⇒ 整棵树 return null。
    截图上直观可见(右下角的实例整体消失)✓ 这是"开关"语义,别拿它当"动画收起"。

  5. ★★★ 纯函数是最便宜的硬判据(而且能覆盖边界)。
    shadeColor 四断言:十六进制、rgb() 输入、上限 clamp、各通道独立 + 补零(#80FF80, -50 ⇒ #407f40)✓
    ⚠️ 顺带发现它只 clamp 上限,负值不 clamp 到 0。

  6. ★★★ 6 处平台分支给出了"该怎么写"的正面样本。
    Platform.OS === 'ios' ? A : B 这种二元判断在非 iOS 平台天然落到 B,只要 B 是合理的默认值就不会出问题;
    而对 TouchableNativeFeedback 这类 Android 专有组件,先判断平台再取组件(getTouchableComponent())就完全安全 ✓

  7. ★★ 判据要读"实例状态",不要只看 dump。
    本轮的定案靠的是 PROBE | F1.state.active = true(周期性读实例 state),而不是布局 dump ✓

  8. ★ 我自己的两次测量失误值得记。
    ① 几何过滤器写死 == 168 ⇒ 漏掉了实测 169px 的右侧实例(给 ±8px 容差);
    ② dump 时机落在"已收起"相位 ⇒ 看到的是"整树 null"却误判为"展开失败"(让被测状态稳定存在够久)✓


本篇用到的库

项内容
三方库react-native-floating-action(上游 1.22.0 的鸿蒙适配版)
适配仓库https://atomgit.com/oh-react-native/react-native-floating-action
适配 TAG1.22.0-ohos-1.0.0
需要 HAR / 权限 / ohpm都不需要(纯 JS)
库类型悬浮操作按钮(FAB):圆形主按钮 + 环绕展开的动作按钮
运行时依赖✅✅ 无(dependencies 为空;只需宿主已有的 prop-types)
prepare / postinstall✅ 都没有
许可证MIT
上游仓库https://github.com/santomegonzalo/react-native-floating-action(★486)
宿主工程RNOH084Demo(测试页 rnAppKey = FloatingActionTestApp)
# 从适配仓库安装(零外部依赖)
npm install "git+https://atomgit.com/oh-react-native/react-native-floating-action.git#1.22.0-ohos-1.0.0" --ignore-scripts
import {FloatingAction} from 'react-native-floating-action';

<FloatingAction
  visible={show}                       // ⚠️ false 且未挂载过 ⇒ 整树 return null
  position="right"                     // "right" | "left" | "center"
  color="#1253bc"
  buttonSize={56}                      // ⇒ 168px(density 3)
  distanceToEdge={30}                  // ⇒ 距边 90px
  actionsPaddingTopBottom={8}
  actions={[
    {name: 'a1', text: '动作一', color: '#D0021B'},
    {name: 'a2', text: '动作二', color: '#0A7D2C'},
  ]}
  onPressMain={opening => console.log('opening=', opening)}   // 参数是 !active
  onPressItem={name => console.log('picked', name)}
  onOpen={() => {}}
  onClose={() => {}}
  onStateChange={st => {}}
/>

// ⚠️ 组件根节点是 position:absolute 的整屏遮罩 ⇒ 要放在滚动容器【之外】
// ⚠️ 点开动作列表在 RNOH 上目前不可见(回调与 state.active 均正常,动作容器停在 opacity 0)
// ⚠️ overrideWithAction=true 时:主按钮直接执行第一个动作并 return,不会再调 onPressMain
// ⚠️ visible=false 是"开关",不是"收起"
# 换页启动测试页 —— force-stop 不能省
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey FloatingActionTestApp

判据的复现方式

# ① 契约 / defaultProps / shadeColor / 两类成员:逐条 PASS/FAIL 日志
hdc shell "hilog -x | grep fab-test" | Select-String 'PASS|FAIL|SUMMARY|NOTE'
#    ⇒ shadeColor 四条:#b20000 / #1253bc / #ff0000 / #407f40
# ② 几何:主按钮 168px(右侧实例可能 169px,给容差)、距边 90px、加号线宽 6px、图标 45px
devecocli ui layout | Select-String '\[(\d+),(\d+),(\d+),(\d+)\]'
# ③ 探针:LayoutAnimation 可用性 + 周期读实例 state(判定 active)
hdc shell "hilog -x | grep fab-test" | Select-String 'A3|LayoutAnimation|PROBE|EVENT'
# ④ 注意:布局 dump 不列 opacity:0 的节点

验证环境

项版本
React Native0.84.1
React19.2.3
RNOH(npm / ohpm)@react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3
Node.js / npmv24.14.0 / 11.9.0
DevEco Studio26.0.0.621
HarmonyOS SDKAPI 26(26.0.0.32)
设备HarmonyOS 7.0.0(26.0.0) Beta2 模拟器 Pura X View(ohos-x64),1320×2232,density 3
本机状态栏高度117px = 39vp
宿主依赖✅ 无需补装(零外部依赖)
宿主 HAP 产物entry-default-signed.hap
本次构建共两次:首次 assembleHap 3 分 37 秒;为加"根因探针"再构建一次 4 分 38 秒
验证规模19 条判据全部通过;几何 4 组精确命中(168/169px、90px、6px、45px);shadeColor 4 条精确断言;两类成员 5/5 与 0/7;根因探针 2 项(LayoutAnimation 可用、state.active = true);4 张截图互异

⚠️ 基线说明:交付包 spec.json 声明 RN 0.88.0 + RNOH 0.84.4(两字段自相矛盾),本次验证在 RN 0.84.1 + RNOH 0.84.3 上完成。
本库无任何已移除写法 ⇒ 契约类结论跨基线成立;但**"动作列表不可见"与 Animated 的执行有关,建议换基线时复验**。


相关社区与项目地址

项目地址
React Native 鸿蒙官方社区(CPF-RN)https://atomgit.com/CPF-RN
RNOH 框架仓库https://atomgit.com/openharmony-RN/ohos_react_native
RNOH 三方库使用文档https://atomgit.com/OpenHarmony-RN/usage-docs
React Native for OpenHarmony 组织https://atomgit.com/oh-react-native
RN 三方库鸿蒙适配清单https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview

欢迎加入 CPF-RN 鸿蒙社区:https://atomgit.com/CPF-RN

React Native for OpenHarmony 组织:https://atomgit.com/oh-react-native

RN 三方库鸿蒙适配清单:https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview

Logo

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

更多推荐