【精通篇】打造React Native鸿蒙跨平台基于HarmonyOS API 24开发高级复合组件库开发系列:Switch 开关(用于在打开和关闭状态之间进行切换)
本文是基于HarmonyOS API 24的进行的React Native跨平台技术实战项目
React Native 跨端鸿蒙开发,行业简称 RNOH(React Native OpenHarmony),是社区 + 华为共建的适配层方案:把 Meta 的 React Native 框架完整移植到鸿蒙(HarmonyOS NEXT / OpenHarmony),让一套 React/JS/TS 代码,同时运行在Android、iOS、鸿蒙手机 / 平板 / PC多端,属于原生级跨端方案,区别于 WebView 套壳网页方案。
简单一句话:前端工程师不用学 ArkTS,用熟悉的 React 语法写业务,底层自动映射成鸿蒙 ArkUI 原生控件,打包成鸿蒙标准 hap 应用上架应用市场。
它的核心定位可以概括为:不改变 React/TypeScript 前端研发习惯,复用现有 RN 业务代码资产,依托鸿蒙系统底层接口做一层高性能中间适配层,将 JSX 组件、JS 业务逻辑映射为鸿蒙原生 ArkUI 控件,最终构建可在手机、平板、车机、智慧屏、PC 等全鸿蒙设备运行、支持上架华为应用市场的原生级应用。和 UniApp、WebView 套壳等网页类跨端方案有本质区别,RNOH 不依赖浏览器内核渲染页面,所有 UI 渲染、手势交互、视图层级全部交给鸿蒙系统原生图形引擎处理,不存在网页性能瓶颈、样式兼容偏差等问题。
兼容完整 React 生态、Hooks、JSX、RN 标准组件,前端工程师无需学习 ArkTS、ArkUI 声明式语法,仅需少量平台兼容代码即可完成多端适配;底层打通鸿蒙 NAPI、ArkUI C 底层接口,兼顾代码复用性与鸿蒙原生能力调用,核心面向存量 RN App 快速新增鸿蒙渠道,是前端团队切入鸿蒙生态最低成本的技术路线。
在 React Native 中进行鸿蒙(HarmonyOS)开发,你需要使用华为提供的 React Native SDK 以及一些特定的插件和工具来支持鸿蒙操作系统。截至我所知的信息,华为并没有官方宣布直接支持 React Native 开发鸿蒙应用,但你可以通过一些间接方法来实现兼容。
- 使用华为提供的 React Native 插件
华为为 Android 开发提供了一些插件和工具,例如华为推送服务、华为云服务等。你可以通过这些服务来间接支持鸿蒙应用开发。例如,可以使用华为的推送服务(HMS Core)来替代 Firebase 推送服务,因为 Firebase 目前不支持鸿蒙。
- 使用华为提供的 HMS Core SDK
华为移动服务(HMS Core)是华为为其设备提供的一系列服务的集合,包括地图、推送、广告等。为了在 React Native 中使用这些服务,你可以使用 react-native-hms-plugin 或其他类似的库。
- 适配鸿蒙操作系统特性
虽然 React Native 主要针对 Android 和 iOS,但你可以通过一些方法适配鸿蒙操作系统:
-
使用动态适配:通过在代码中判断当前系统类型,动态加载不同的代码或组件。例如,使用条件编译的方式:
if (Platform.OS === 'harmonyos') { // 加载鸿蒙特定的代码 } else { // 加载 Android 或 iOS 的代码 } -
使用鸿蒙特有的 API:如果有必要使用鸿蒙特有的 API,可以通过 JNI(Java Native Interface)或 C++ 桥接的方式调用。
- 测试和调试
由于鸿蒙操作系统与 Android 系统在很多方面相似,你可以使用 Android 设备的模拟器或者真实的鸿蒙设备进行测试。确保你的 React Native 应用可以在这些设备上正常运行。
- 官方支持和文档
建议关注华为开发者论坛和官方文档,因为这可能会提供最新的支持和开发指南。华为可能会发布关于如何在 React Native 中使用 HMS Core 的具体指导。
结论
虽然目前没有直接的 React Native SDK 支持鸿蒙开发,但通过使用华为提供的 HMS Core 和其他插件,以及适配现有代码的策略,你可以在 React Native 项目中实现部分对鸿蒙的支持。重要的是持续关注华为的官方公告和开发者社区的动态,以获取最新的支持和工具。未来随着技术的发展,可能会有更多针对 React Native 的直接支持出现。
真实案例演示代码:
import React, { useState } from 'react';
import { View, Text, StyleSheet, ScrollView, Dimensions, TouchableOpacity, Animated } from 'react-native';
// Simple Icon Component using Unicode symbols
interface IconProps {
name: string;
size?: number;
color?: string;
style?: object;
}
const Icon: React.FC<IconProps> = ({
name,
size = 24,
color = '#333333',
style
}) => {
const getIconSymbol = () => {
switch (name) {
case 'check': return '✓';
case 'close': return '✕';
case 'wifi': return '📶';
case 'bluetooth': return 'luetooth';
case 'notification': return '🔔';
case 'silent': return '🔇';
case 'sound': return '🔊';
case 'dark': return '🌙';
case 'light': return '☀️';
default: return '●';
}
};
return (
<View style={[{ width: size, height: size, justifyContent: 'center', alignItems: 'center' }, style]}>
<Text style={{ fontSize: size * 0.8, color, includeFontPadding: false, textAlign: 'center' }}>
{getIconSymbol()}
</Text>
</View>
);
};
// Switch Component
interface SwitchProps {
value: boolean;
onValueChange: (value: boolean) => void;
disabled?: boolean;
size?: 'small' | 'medium' | 'large';
activeColor?: string;
inactiveColor?: string;
showIcons?: boolean;
onIcon?: string;
offIcon?: string;
}
const Switch: React.FC<SwitchProps> = ({
value,
onValueChange,
disabled = false,
size = 'medium',
activeColor = '#1890ff',
inactiveColor = '#cccccc',
showIcons = false,
onIcon,
offIcon
}) => {
const getSizeStyles = () => {
switch (size) {
case 'small':
return { width: 40, height: 20, thumbSize: 16 };
case 'large':
return { width: 60, height: 30, thumbSize: 26 };
default:
return { width: 50, height: 25, thumbSize: 21 };
}
};
const { width, height, thumbSize } = getSizeStyles();
const translateX = value ? width - thumbSize - 4 : 4;
return (
<TouchableOpacity
style={[
styles.switchContainer,
{
width,
height,
backgroundColor: disabled
? '#f5f5f5'
: value
? activeColor
: inactiveColor,
borderRadius: height / 2,
opacity: disabled ? 0.6 : 1
}
]}
onPress={() => !disabled && onValueChange(!value)}
activeOpacity={disabled ? 1 : 0.8}
disabled={disabled}
>
<Animated.View
style={[
styles.thumb,
{
width: thumbSize,
height: thumbSize,
borderRadius: thumbSize / 2,
transform: [{ translateX }]
}
]}
>
{showIcons && (
<View style={styles.iconContainer}>
{value && onIcon && (
<Icon name={onIcon} size={thumbSize * 0.6} color={activeColor} />
)}
{!value && offIcon && (
<Icon name={offIcon} size={thumbSize * 0.6} color="#999999" />
)}
</View>
)}
</Animated.View>
</TouchableOpacity>
);
};
// Main App Component
const SwitchComponentApp = () => {
const [wifiEnabled, setWifiEnabled] = useState(true);
const [bluetoothEnabled, setBluetoothEnabled] = useState(false);
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
const [darkModeEnabled, setDarkModeEnabled] = useState(false);
const [soundEnabled, setSoundEnabled] = useState(true);
const [autoUpdateEnabled, setAutoUpdateEnabled] = useState(true);
const [locationEnabled, setLocationEnabled] = useState(false);
return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerTitle}>开关组件</Text>
<Text style={styles.headerSubtitle}>美观实用的开关控件</Text>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>基础用法</Text>
<View style={styles.switchGroupsContainer}>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<Icon name="wifi" size={24} color="#1890ff" style={styles.switchIcon} />
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>Wi-Fi</Text>
<Text style={styles.switchDesc}>无线网络连接</Text>
</View>
<Switch
value={wifiEnabled}
onValueChange={setWifiEnabled}
activeColor="#1890ff"
showIcons
onIcon="check"
offIcon="close"
/>
</View>
</View>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<Icon name="bluetooth" size={24} color="#52c41a" style={styles.switchIcon} />
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>蓝牙</Text>
<Text style={styles.switchDesc}>蓝牙设备连接</Text>
</View>
<Switch
value={bluetoothEnabled}
onValueChange={setBluetoothEnabled}
activeColor="#52c41a"
showIcons
onIcon="check"
offIcon="close"
/>
</View>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>系统设置</Text>
<View style={styles.switchGroupsContainer}>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<Icon name="notification" size={24} color="#fa8c16" style={styles.switchIcon} />
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>通知</Text>
<Text style={styles.switchDesc}>应用消息提醒</Text>
</View>
<Switch
value={notificationsEnabled}
onValueChange={setNotificationsEnabled}
activeColor="#fa8c16"
showIcons
onIcon="check"
offIcon="close"
/>
</View>
</View>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<Icon name="dark" size={24} color="#722ed1" style={styles.switchIcon} />
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>夜间模式</Text>
<Text style={styles.switchDesc}>深色主题界面</Text>
</View>
<Switch
value={darkModeEnabled}
onValueChange={setDarkModeEnabled}
activeColor="#722ed1"
showIcons
onIcon="dark"
offIcon="light"
/>
</View>
</View>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<Icon name="sound" size={24} color="#ff4d4f" style={styles.switchIcon} />
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>声音</Text>
<Text style={styles.switchDesc}>系统声音播放</Text>
</View>
<Switch
value={soundEnabled}
onValueChange={setSoundEnabled}
activeColor="#ff4d4f"
showIcons
onIcon="sound"
offIcon="silent"
/>
</View>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>应用设置</Text>
<View style={styles.switchGroupsContainer}>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>自动更新</Text>
<Text style={styles.switchDesc}>应用自动更新功能</Text>
</View>
<Switch
value={autoUpdateEnabled}
onValueChange={setAutoUpdateEnabled}
activeColor="#1890ff"
/>
</View>
</View>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>位置服务</Text>
<Text style={styles.switchDesc}>获取当前位置信息</Text>
</View>
<Switch
value={locationEnabled}
onValueChange={setLocationEnabled}
activeColor="#52c41a"
disabled
/>
</View>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>不同尺寸</Text>
<View style={styles.switchGroupsContainer}>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>小尺寸</Text>
</View>
<Switch
value={true}
onValueChange={() => {}}
size="small"
activeColor="#1890ff"
/>
</View>
</View>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>中等尺寸</Text>
</View>
<Switch
value={true}
onValueChange={() => {}}
size="medium"
activeColor="#52c41a"
/>
</View>
</View>
<View style={styles.switchGroup}>
<View style={styles.switchRow}>
<View style={styles.switchInfo}>
<Text style={styles.switchLabel}>大尺寸</Text>
</View>
<Switch
value={true}
onValueChange={() => {}}
size="large"
activeColor="#722ed1"
/>
</View>
</View>
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>功能演示</Text>
<View style={styles.demosContainer}>
<View style={styles.demoItem}>
<Icon name="check" size={24} color="#1890ff" style={styles.demoIcon} />
<View>
<Text style={styles.demoTitle}>状态切换</Text>
<Text style={styles.demoDesc}>支持开启和关闭两种状态</Text>
</View>
</View>
<View style={styles.demoItem}>
<Icon name="wifi" size={24} color="#52c41a" style={styles.demoIcon} />
<View>
<Text style={styles.demoTitle}>图标集成</Text>
<Text style={styles.demoDesc}>支持自定义图标显示</Text>
</View>
</View>
<View style={styles.demoItem}>
<Icon name="sound" size={24} color="#722ed1" style={styles.demoIcon} />
<View>
<Text style={styles.demoTitle}>多种尺寸</Text>
<Text style={styles.demoDesc}>支持小、中、大三种尺寸</Text>
</View>
</View>
</View>
</View>
<View style={styles.usageSection}>
<Text style={styles.sectionTitle}>使用方法</Text>
<View style={styles.codeBlock}>
<Text style={styles.codeText}>{'<Switch'}</Text>
<Text style={styles.codeText}> value={'{isEnabled}'}</Text>
<Text style={styles.codeText}> onValueChange={'{setIsEnabled}'}</Text>
<Text style={styles.codeText}> activeColor="#1890ff"{'\n'}/></Text>
</View>
<Text style={styles.description}>
Switch组件提供了完整的开关功能,包括状态切换、自定义颜色、多种尺寸和图标支持。
通过value控制当前状态,onValueChange处理状态变化,支持自定义样式和图标。
</Text>
</View>
<View style={styles.featuresSection}>
<Text style={styles.sectionTitle}>功能特性</Text>
<View style={styles.featuresList}>
<View style={styles.featureItem}>
<Icon name="check" size={20} color="#1890ff" style={styles.featureIcon} />
<Text style={styles.featureText}>状态切换</Text>
</View>
<View style={styles.featureItem}>
<Icon name="wifi" size={20} color="#52c41a" style={styles.featureIcon} />
<Text style={styles.featureText}>图标集成</Text>
</View>
<View style={styles.featureItem}>
<Icon name="sound" size={20} color="#722ed1" style={styles.featureIcon} />
<Text style={styles.featureText}>多种尺寸</Text>
</View>
<View style={styles.featureItem}>
<Icon name="dark" size={20} color="#fa8c16" style={styles.featureIcon} />
<Text style={styles.featureText}>状态控制</Text>
</View>
</View>
</View>
<View style={styles.footer}>
<Text style={styles.footerText}>© 2023 开关组件 | 现代化UI组件库</Text>
</View>
</ScrollView>
);
};
const { width } = Dimensions.get('window');
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8f9fa',
},
header: {
backgroundColor: '#ffffff',
paddingVertical: 30,
paddingHorizontal: 20,
marginBottom: 10,
borderBottomWidth: 1,
borderBottomColor: '#e9ecef',
},
headerTitle: {
fontSize: 28,
fontWeight: '700',
color: '#212529',
textAlign: 'center',
marginBottom: 5,
},
headerSubtitle: {
fontSize: 16,
color: '#6c757d',
textAlign: 'center',
},
section: {
marginBottom: 25,
},
sectionTitle: {
fontSize: 20,
fontWeight: '700',
color: '#212529',
paddingHorizontal: 20,
paddingBottom: 15,
},
switchGroupsContainer: {
backgroundColor: '#ffffff',
marginHorizontal: 15,
borderRadius: 12,
padding: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
marginBottom: 10,
},
switchGroup: {
marginBottom: 15,
},
switchGroupLast: {
marginBottom: 0,
},
switchRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
switchIcon: {
marginRight: 15,
},
switchInfo: {
flex: 1,
},
switchLabel: {
fontSize: 16,
fontWeight: '500',
color: '#212529',
marginBottom: 3,
},
switchDesc: {
fontSize: 14,
color: '#6c757d',
},
demosContainer: {
backgroundColor: '#ffffff',
marginHorizontal: 15,
borderRadius: 15,
padding: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
},
demoItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 20,
},
demoItemLast: {
marginBottom: 0,
},
demoIcon: {
marginRight: 15,
},
demoTitle: {
fontSize: 16,
fontWeight: '600',
color: '#212529',
marginBottom: 3,
},
demoDesc: {
fontSize: 14,
color: '#6c757d',
},
usageSection: {
backgroundColor: '#ffffff',
marginHorizontal: 15,
borderRadius: 15,
padding: 20,
marginBottom: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
},
codeBlock: {
backgroundColor: '#2b3541',
borderRadius: 8,
padding: 15,
marginBottom: 15,
},
codeText: {
fontFamily: 'monospace',
color: '#e9ecef',
fontSize: 14,
lineHeight: 22,
},
description: {
fontSize: 15,
color: '#495057',
lineHeight: 22,
},
featuresSection: {
backgroundColor: '#ffffff',
marginHorizontal: 15,
borderRadius: 15,
padding: 20,
marginBottom: 20,
elevation: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
},
featuresList: {
paddingLeft: 10,
},
featureItem: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 15,
},
featureIcon: {
marginRight: 15,
},
featureText: {
fontSize: 16,
color: '#212529',
},
footer: {
paddingVertical: 20,
alignItems: 'center',
},
footerText: {
color: '#adb5bd',
fontSize: 14,
},
// Switch Styles
switchContainer: {
justifyContent: 'center',
position: 'relative',
},
thumb: {
position: 'absolute',
backgroundColor: '#ffffff',
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.2,
shadowRadius: 2,
},
iconContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
export default SwitchComponentApp;
这段React Native Switch组件的逻辑在鸿蒙开发中对应着ArkUI的Toggle组件实现模式。从鸿蒙架构角度分析,Switch组件的核心状态管理通过@State装饰器实现,value属性对应Toggle组件的isOn状态,当用户点击开关时触发onValueChange回调,这与鸿蒙的onChange事件机制完全对应。
在鸿蒙开发中,Switch组件的尺寸适配通过ResourceManager实现,类似代码中的getSizeStyles方法。鸿蒙Toggle组件支持selectedColor和switchPointColor属性分别对应activeColor和thumb颜色控制。图标显示逻辑通过条件渲染实现,当showIcons为true时,根据开关状态显示对应的Symbol图标。
动画效果在鸿蒙中通过animateTo方法实现,translateX变换对应鸿蒙的translate属性。边界控制通过disabled属性实现,当组件禁用时自动降低透明度并阻止交互事件。样式系统采用鸿蒙的@Styles装饰器集中管理,实现统一的视觉效果。
组件通信采用受控模式,通过@Prop装饰器接收父组件的状态值,通过自定义事件向上传递状态变更。这种设计模式在鸿蒙应用中广泛用于设置页面、功能开关等场景。
需要我将这个Switch组件转换为完整的鸿蒙ArkTS实现吗?只需回复"转换"即可。
安装DevEco Studio程序

选择目标安装目录:

设置环境变量,但是需要重启一下:

新建一个空白模板:

设置API为24的模板项目:
初始化项目,自动下载相关依赖:

打包
接下来通过打包命令npn run harmony将reactNative的代码打包成为bundle,这样可以进行在开源鸿蒙OpenHarmony中进行使用。

打包之后再将打包后的鸿蒙OpenHarmony文件拷贝到鸿蒙的DevEco-Studio工程目录去:

最后运行效果图如下显示:

更多推荐



所有评论(0)