React Native鸿蒙跨平台数据使用监控应用技术,通过setInterval每5秒更新一次数据使用情况和套餐使用情况,模拟了真实应用中的数据监控场景
本文介绍了一款基于React Native开发的跨平台流量监控应用DataUsageMonitor。该应用采用函数式组件架构,通过useState和useEffect实现轻量级状态管理,支持流量套餐监控、应用级统计、用量警报和设置管理等核心功能。 技术亮点包括: 使用TypeScript接口确保数据类型安全 通过useEffect模拟实时数据更新 动态进度条颜色反馈流量使用状态 模块化设计实现代码
DataUsageMonitor 组件采用了现代 React 函数组件架构,结合 useState 和 useEffect Hooks 实现了复杂的状态管理和副作用处理。组件通过多个状态变量管理不同类型的数据:activeTab 控制当前激活的标签页,dataUsage 存储应用数据使用情况,dataPlan 存储数据套餐信息,alerts 存储警告信息,settings 存储应用设置。
在类型定义上,组件使用了 TypeScript 接口定义了 DataUsage、DataPlan 和 Alert 类型,确保了类型安全。这种类型定义在跨端开发中尤为重要,可以确保在不同平台上的数据结构一致性。
useEffect Hook 的使用是该组件的一个技术亮点,它实现了模拟实时数据更新功能。通过 setInterval 每5秒更新一次数据使用情况和套餐使用情况,模拟了真实应用中的数据监控场景。同时,通过返回清理函数 clearInterval,避免了内存泄漏,这是 React 中处理副作用的最佳实践。
布局设计
组件采用了标签页导航结构,通过 activeTab 状态控制不同内容的显示。主页内容使用了 ScrollView 确保在内容较长时可以滚动查看。布局设计清晰明了,主要包含以下几个部分:
- 套餐状态卡片:展示当前数据套餐的使用情况,包括已用流量、总流量、剩余流量和重置日期。
- 进度条:直观显示流量使用百分比,通过
calculatePercentage函数计算百分比,通过getProgressColor函数根据百分比动态改变颜色,增强了视觉反馈。 - 应用数据使用列表:通过
renderAppItem函数渲染每个应用的图标、名称、包名和数据使用情况。 - 警告列表:通过
renderAlertItem函数渲染不同严重程度的警告信息,使用不同的样式区分严重程度。
样式设计上,组件使用了 StyleSheet.create 集中管理样式,避免了直接内联样式可能带来的性能问题。样式命名规范清晰,如 planCard、appItem、alertItem 等,提高了代码的可读性。同时,通过条件样式(如 item.severity === 'high' && styles.alertHigh)实现了不同状态的视觉区分。
组件实现了标签页切换功能,通过点击不同标签页切换到对应的内容。实时数据更新功能为用户提供了动态的流量使用监控体验,每5秒自动更新一次数据,模拟了真实的网络监控场景。
进度条的颜色动态变化是一个重要的用户体验设计,当流量使用达到不同阈值时,进度条会显示不同的颜色:绿色(低于75%)、黄色(75%-90%)、红色(高于90%),为用户提供了直观的视觉警告。
警告列表根据严重程度使用不同的样式,高严重度的警告使用红色背景,中等严重度的使用黄色背景,低严重度的使用蓝色背景,帮助用户快速识别重要的警告信息。
在 React Native 与鸿蒙系统跨端开发中,该组件展现了多项兼容性设计:
-
组件选择:使用了
View、Text、ScrollView等基础组件,这些组件在 React Native 和鸿蒙系统中都有对应的实现。 -
样式适配:通过
StyleSheet定义样式,避免了直接内联样式可能带来的性能问题,同时确保了在不同平台上的一致表现。 -
数据管理:使用了 React 的状态管理机制,在鸿蒙系统中可以通过相应的状态管理机制(如
@State装饰器)实现类似的功能。 -
类型定义:使用了 TypeScript 类型定义,确保了在不同平台上的数据结构一致性,这在跨端开发中尤为重要。
-
模拟数据:使用了硬编码的模拟数据,避免了依赖特定平台的 API 获取真实数据,这在跨端开发的早期阶段非常有用。
-
状态管理优化:使用了
useStateHook 进行状态管理,对于这种中等复杂度的组件,避免了引入 Redux 等重型状态管理库的必要性。 -
副作用处理:通过
useEffectHook 处理副作用,并且在组件卸载时清理定时器,避免了内存泄漏。 -
渲染优化:使用了函数式更新(如
setDataUsage(prev => prev.map(...))),确保了状态更新的正确性,同时避免了不必要的渲染。 -
样式复用:通过样式对象的复用(如
styles.appItem多次使用),减少了代码冗余,提高了代码的可维护性。 -
计算缓存:在
useEffect中进行数据计算,避免了在渲染过程中进行复杂计算,提高了渲染性能。
组件的代码结构清晰,逻辑分明,主要分为以下几个部分:
- 类型定义:集中定义了
DataUsage、DataPlan和Alert类型,提高了代码的类型安全性和可读性。 - 状态定义:集中定义了组件的状态变量,便于查看和管理。
- 副作用处理:使用
useEffectHook 处理实时数据更新等副作用。 - 辅助函数:定义了
calculatePercentage和getProgressColor等辅助函数,提高了代码的复用性和可读性。 - 渲染函数:定义了
renderAppItem、renderAlertItem和renderHomeContent等渲染函数,将复杂的渲染逻辑拆分为可管理的小块。 - JSX 结构:根据
activeTab状态渲染不同的内容,结构清晰。 - 样式定义:使用
StyleSheet.create集中管理样式,提高了代码的可读性和可维护性。
这种结构设计不仅便于开发者理解和维护代码,也为后续的功能扩展和跨平台适配提供了良好的基础。
在将该组件适配到鸿蒙系统时,需要注意以下几点:
-
组件映射:将 React Native 的
View、Text、ScrollView等组件映射到鸿蒙系统的对应组件。例如,View对应鸿蒙的Component,Text对应鸿蒙的Text,ScrollView对应鸿蒙的ListContainer。 -
样式转换:将
StyleSheet中的样式转换为鸿蒙系统支持的样式格式。例如,React Native 的flexDirection: 'row'对应鸿蒙的flexDirection: FlexDirection.Row。 -
状态管理:鸿蒙系统的状态管理机制与 React Native 有所不同,需要进行适当的调整。例如,可以使用鸿蒙的
@State装饰器替代useStateHook,使用@Watch装饰器替代useEffectHook。 -
副作用处理:鸿蒙系统中处理定时器等副作用的方式与 React Native 不同,需要使用鸿蒙的生命周期方法或相应的 API。
-
性能优化:根据鸿蒙系统的特性,进行针对性的性能优化,确保组件在鸿蒙设备上运行流畅。例如,合理使用鸿蒙的缓存机制和渲染优化策略。
DataUsageMonitor 组件展示了一个功能完整、结构清晰的 React Native 数据监控应用实现,涵盖了状态管理、布局设计、交互处理等多个方面的技术点。通过合理的组件架构和状态管理,以及对跨端兼容性的考虑,该组件不仅在 React Native 环境下运行良好,也为后续的鸿蒙系统适配奠定了基础。
在实际开发中,我们可以借鉴这种组件设计思路,结合具体业务需求,构建出更加丰富、高效的跨平台应用。同时,通过不断优化和扩展,提升应用的用户体验和性能表现,实现真正意义上的一次开发,多端运行。
该组件的设计理念和实现方式,体现了现代移动应用开发的最佳实践,特别是在跨端开发领域,为我们提供了宝贵的参考。
本次实现的
DataUsageMonitor是一款移动设备流量管理类核心应用,聚焦流量套餐监控、应用级流量统计、用量警报提醒、流量设置管理四大核心能力,同时实现模拟实时流量数据更新、数据使用趋势可视化、多维度数据排序等增强特性,是工具类应用中数据监控与管理场景的典型落地。
该应用基于React Native纯原生基础组件开发,无第三方依赖,通过TypeScript接口规范数据结构,结合useState+useEffect实现轻量状态管理与实时数据模拟,全程采用Flex弹性布局完成卡片式统计、流式快捷操作、柱状趋势图、列表式数据展示、开关式设置等工具类应用高频布局,整体设计贴合移动端工具类应用的交互与视觉规范。
从跨端视角来看,该应用严格遵循React Native鸿蒙跨端友好开发原则:Base64图标资源全局统一管理可跨端直接复用,Flex布局无缝映射为ArkUI通用布局,原生基础组件与API可通过react-native-harmony桥接层一键转换,纯JS/TS业务逻辑(数据计算、排序、实时更新)无需修改即可在鸿蒙端运行,模块化样式设计让鸿蒙端适配仅需轻量微调。作为工具类数据监控应用的典型案例,其开发思路可复用于流量管理、设备监控、数据统计等各类工具类应用开发,以下从整体架构设计、核心技术实现、跨端友好开发细节、鸿蒙端实操适配要点四个维度,深度解读该代码的技术设计与跨端适配逻辑。
工具类数据监控应用:
工具类数据监控应用的核心设计诉求是数据展示清晰、交互高效、布局规整、逻辑可复用,同时需保证跨端适配时修改范围高度收敛。本次DataUsageMonitor应用遵循高内聚、模块化、数据驱动、跨端通用的设计原则,将应用拆分为头部导航、动态内容区、底部标签导航三大核心模块,按功能维度将内容区拆分为首页、应用、警报、设置四个子页面,通过标签切换实现页面无刷新切换,所有视图渲染、样式设计、业务逻辑均收敛在单个函数式组件中,结合TypeScript接口实现数据结构的强类型约束。
这种设计方式既保证了工具类应用的功能连贯性——用户可在一个应用内完成流量监控、统计、提醒、设置的一站式操作,又契合React Native鸿蒙跨端规范:所有修改均集中在组件内部,无跨组件的复杂数据共享与逻辑依赖,大幅降低鸿蒙端改造成本,同时极简的组件结构让桥接层的解析与映射效率更高,保证应用在鸿蒙端的渲染与交互性能。
跨端友好的基础设计细节
- 极简依赖选型:仅引入React核心库、
useState/useEffect轻量Hook与RN原生基础组件(SafeAreaView/View/Text/TouchableOpacity/ScrollView/FlatList/Image),以及跨端通用的Dimensions/AlertAPI,无任何第三方UI库、状态管理库或端侧特有依赖。这些基础组件与API均已在华为开源的react-native-harmony桥接层中实现与ArkUI的一一映射,无需开发自定义桥接模块即可完成基础适配。 - Base64资源全局统一管理:将首页、监控、用量、警报等工具类高频小图标封装在全局
ICONS_BASE64常量对象中,以Base64格式替代传统本地图片资源。该方式从根本上解决了跨端资源适配的核心痛点——无需在iOS/Android/鸿蒙各端分别配置资源目录与路径,实现一次编码,多端复用,且RN与ArkUI的Image组件均原生支持Base64格式的uri加载,桥接层兼容无成本。 - TypeScript强类型约束:通过
DataUsage/DataPlan/Alert三大接口规范核心数据结构,明确每个字段的类型与含义,避免因数据类型错误导致的跨端渲染问题。TypeScript的类型检查仅在开发阶段生效,编译后生成纯JS代码,可直接在鸿蒙端运行,既保证开发效率,又提升跨端代码的健壮性。 - 轻量状态与纯函数逻辑:通过少量状态(标签激活态、各类业务数据、设置项状态)覆盖所有交互需求,无复杂的状态流转与数据共享。所有业务逻辑(数据计算、排序、实时更新、样式映射)均通过纯函数实现,与平台无关,跨端时可直接复用。
DataUsageMonitor数据使用监管助手:
本次实现的DataUsageMonitor数据使用监管助手,核心覆盖套餐流量监控、应用流量统计、用量警报提醒、流量设置管理四大核心能力,同时实现模拟实时数据更新、数据趋势可视化、多维度数据排序、样式动态映射四大增强特性,所有实现均基于RN原生基础组件与跨端通用的Flex布局,从数据、布局、交互、可视化四个维度践行跨端友好原则,核心逻辑可直接映射为ArkUI实现,仅需轻量样式微调即可贴合鸿蒙端设计规范。
TypeScript接口设计:跨端通用的强类型数据结构规范
工具类应用的核心是数据驱动视图,清晰、规范的数据结构是跨端复用的基础。本次通过TypeScript三大接口DataUsage/DataPlan/Alert,分别规范应用流量数据、套餐流量数据、用量警报数据的结构,明确每个字段的类型、含义与单位,同时为设置项定义统一的状态对象,实现数据结构的强类型约束。
核心设计亮点:
- 字段语义化与单位统一:所有数据字段均采用语义化命名(如
mobileData/wifiData/totalData),流量单位统一为MB,时间戳格式统一为YYYY-MM-DD HH:mm,避免跨端因数据格式不统一导致的解析错误与展示混乱。 - 数据维度分层:将应用流量数据拆分为移动、WiFi、总计三个维度,套餐数据拆分为总计、已用、剩余、重置日期四个维度,警报数据拆分为标题、内容、时间、严重程度四个维度,贴合流量管理的业务逻辑,同时让视图渲染逻辑更简洁。
- 严重程度枚举化:警报数据的
severity字段采用'low' | 'medium' | 'high'联合类型,替代魔法值,既提升代码可读性,又为后续动态样式映射(不同严重程度对应不同颜色)提供统一的判断依据。 - 状态与数据分离:将静态业务数据(应用列表、套餐信息)与动态交互状态(标签激活态、设置开关态)分离管理,让数据更新与视图渲染更高效,跨端时可独立调整状态管理逻辑,不影响核心业务数据。
TypeScript接口仅在开发阶段提供类型检查,编译后生成的纯JS代码可直接在鸿蒙端运行,无任何跨端兼容问题,同时规范的数据结构让鸿蒙端的ArkUI组件渲染逻辑与RN端完全一致,实现数据层的跨端无缝复用。
模拟实时数据更新:
流量管理应用的核心特性是实时性,本次通过useEffect结合定时器,实现跨端通用的模拟实时数据更新,模拟移动应用与套餐流量的持续消耗,让应用更贴近真实业务场景,同时实现定时器的自动销毁,避免内存泄漏。
核心实现逻辑:
- 定时器创建与销毁:在
useEffect中通过setInterval创建定时器,每5秒执行一次数据更新,同时返回清理函数clearInterval,在组件卸载时自动销毁定时器,遵循RN的组件生命周期规范,避免内存泄漏。该逻辑为纯JS实现,与平台无关,RN与ArkUI的定时器API完全兼容,跨端可直接复用。 - 不可变数据更新:使用数组与对象的展开运算符(
...)实现不可变数据更新,如setDataUsage(prev => prev.map(app => ({...app, mobileData: app.mobileData + Math.random() * 0.1}))),避免直接修改原数据导致的视图不刷新问题。不可变数据更新是React的核心设计原则,也是跨端通用的开发规范,鸿蒙端的ArkUI同样遵循该原则,保证视图更新的一致性。 - 数据更新逻辑贴合业务:移动流量更新幅度(
Math.random() * 0.1)大于WiFi流量(Math.random() * 0.05),贴合真实场景中移动流量消耗更快的业务逻辑,套餐已用流量缓慢递增,剩余流量自动同步递减,保证数据的一致性与合理性。
该实时数据模拟逻辑为纯JS实现,无任何端侧特有API,跨端时可直接复用,若需对接真实原生数据(如设备流量统计),仅需替换定时器内的模拟逻辑为原生桥接的真实数据接口,核心的状态更新与视图渲染逻辑无需修改。
动态样式映射:纯函数实现跨端通用的样式与数据联动
工具类应用的视觉反馈是核心交互体验,本次通过两个纯函数calculatePercentage/getProgressColor,实现数据到样式的动态映射——套餐使用百分比决定进度条宽度,百分比决定进度条颜色,警报严重程度决定警报卡片的左侧边框颜色,让视图样式随数据动态变化,提升用户体验。
核心实现逻辑:
- 百分比计算函数:
calculatePercentage接收已用与总计数据,返回计算后的百分比,并通过Math.min(100, ...)限制百分比不超过100%,避免进度条宽度超出容器,保证布局规整。该函数为纯数学计算,无任何端侧依赖,跨端可直接复用。 - 颜色动态映射函数:
getProgressColor根据百分比返回不同颜色(绿色<75%、黄色75%-90%、红色≥90%),贴合用户对用量的视觉感知;警报卡片通过样式条件判断(item.severity === 'high' && styles.alertHigh),为不同严重程度的警报匹配不同颜色的左侧边框,实现样式与数据的联动。 - 样式映射纯函数化:所有动态样式逻辑均通过纯函数或条件判断实现,无复杂的样式计算,RN与ArkUI均支持通过状态与条件判断动态绑定样式,跨端时可直接复用映射逻辑,仅需微调颜色值即可贴合鸿蒙端设计规范。
Flex布局:
本次应用充分发挥RN Flex布局的灵活性,实现工具类应用的六大高频布局,所有布局均为跨端通用实现,无任何平台特有代码,可直接映射为ArkUI的Flex布局,实现与RN端高度一致的展示效果。
六大核心布局实现:
- 卡片式统计布局:套餐流量统计采用
planCard卡片容器,内部按标题→用量→剩余→进度条的垂直顺序布局,进度条通过flexDirection: row实现进度条与百分比文字的横向排列,flex:1让进度条占满剩余宽度,保证不同设备的自适应。 - 流式快捷操作布局:快速操作按钮通过
flexDirection: row+justifyContent: 'space-between'实现横向流式排列,结合Dimensions.get('window').width计算每个按钮的宽度(width: width / 4.5),保证不同设备上按钮的等分布局,无挤压或留白。 - 柱状趋势图布局:本周数据趋势通过纯Flex布局实现轻量化柱状图,无需第三方可视化库——
trendChart容器为横向Flex,每个trendColumn为垂直Flex(柱状条+日期标签),柱状条的高度通过动态百分比(height:${value}%``)控制,不同数值对应不同高度,超过70%的柱状条自动变为红色,实现可视化与视觉提醒的结合。 - 列表式数据展示布局:应用流量列表与警报列表采用RN原生
FlatList组件,结合自定义renderItem实现“图标+信息+用量”的横向列表项布局,flex:1让信息区域占满剩余宽度,保证不同设备的自适应,FlatList的scrollEnabled: false实现非滚动列表,贴合局部数据展示的需求。 - 开关式设置布局:设置项采用
settingRow横向Flex布局,实现“标签+开关/值”的两端对齐,开关组件通过TouchableOpacity自定义实现,结合状态动态绑定激活样式,无需第三方开关组件,跨端可直接映射为ArkUI的开关组件。 - 底部标签导航布局:底部导航通过
flexDirection: row+justifyContent: 'space-around'实现四个标签的等分布局,每个标签为垂直Flex(图标+文字),结合激活状态动态绑定图标与文字的颜色,实现标签切换的视觉反馈,贴合移动端应用的导航习惯。
所有布局均采用Flex核心属性(flexDirection/justifyContent/alignItems/flex:1)实现,无任何硬编码的像素值布局,保证不同尺寸设备的自适应,同时RN与ArkUI的Flex布局属性完全一致,跨端时可直接复用布局逻辑。
标签页切换:
应用采用底部标签导航实现首页/应用/警报/设置四个子页面的无刷新切换,通过activeTab状态控制不同子页面的渲染,结合TouchableOpacity实现标签的点击切换,逻辑简洁且跨端通用。
核心实现逻辑:
- 状态定义:定义
activeTab状态,类型为'home' | 'apps' | 'alerts' | 'settings'联合类型,限制标签值的范围,避免无效值导致的渲染错误。 - 条件渲染:通过条件判断
{activeTab === 'home' && renderHomeContent()},根据activeTab的值渲染对应的子页面内容,所有子页面均通过独立的渲染函数实现,让代码结构更清晰,跨端时可独立调整每个子页面的布局,不影响其他页面。 - 点击切换:底部导航的每个标签绑定
onPress事件,通过setActiveTab修改激活状态,实现标签的无刷新切换,无任何端侧特有交互逻辑,RN与ArkUI均通过点击事件修改状态,实现视图的重新渲染。
这种轻量的标签页切换方式,无需第三方路由库,适合工具类应用的简单页面切换场景,跨端可直接复用,若需实现更复杂的页面跳转,可集成React Navigation,其同样支持鸿蒙端适配。
样式设计是React Native鸿蒙跨端适配的核心细节,本次实现遵循模块化样式、动态化样式、跨端通用规范三大原则,所有样式均基于RN内置的StyleSheet.create创建,采用驼峰命名法,无平台特有样式属性,通过基础样式+差异化样式覆盖实现样式复用,通过动态样式绑定实现数据与样式的联动,让鸿蒙端适配仅需轻量微调,无需重构样式逻辑。
模块化
按应用的功能模块与视觉元素定义独立的样式类,如planCard(套餐卡片)、quickAction(快捷操作按钮)、appItem(应用列表项)、alertItem(警报列表项)、settingRow(设置项)等,每个样式类仅负责对应模块/元素的样式,实现样式与视图的一一对应。
这种设计让鸿蒙端样式微调时,修改范围高度收敛——例如需要调整鸿蒙端的卡片圆角,仅需修改planCard/trendCard/infoCard的borderRadius属性即可,无需修改其他模块的样式,大幅提升适配效率。同时,通过状态动态绑定样式实现差异化展示,如toggleActive(开关激活态)、navIconActive(导航图标激活态)、alertHigh(高严重程度警报),让样式复用率更高,减少样式冗余。
动态化
结合业务数据与交互状态,实现样式的动态化绑定,让视图随数据与状态变化,提升用户体验,所有动态样式均为跨端通用实现:
- 数据驱动动态样式:进度条的宽度与背景色、趋势图柱状条的高度与背景色、警报卡片的左侧边框色,均通过业务数据动态计算,实现数据与样式的联动。
- 状态驱动动态样式:开关的激活态、底部导航的图标与文字颜色、标签页的激活状态,均通过交互状态动态绑定,实现交互与样式的联动。
- 设备自适应样式:通过
Dimensions.get('window').width计算快捷操作按钮的宽度,实现不同设备的自适应布局,避免硬编码像素值导致的布局错乱,RN与ArkUI均支持获取设备尺寸,跨端可直接复用。
样式规范
- 驼峰命名法:所有样式属性均采用RN标准的驼峰命名法(如
backgroundColor/borderRadius/paddingVertical),与ArkUI的样式命名规范完全一致,鸿蒙端迁移时无需修改任何样式属性名。 - 通用样式属性:所有使用的样式属性(
flex/alignItems/justifyContent/borderRadius/shadow/margin/padding/elevation)均为RN与ArkUI的通用属性,无iOS/Android/鸿蒙平台特有属性,桥接层可直接映射。 - 视觉风格统一:整体采用工具类应用的经典视觉风格——白色卡片+浅灰色背景+圆角+轻量阴影,符合移动端用户的视觉习惯,同时阴影与
elevation结合使用,保证iOS与Android端的阴影展示效果一致,鸿蒙端可直接复用该视觉风格。 - 交互元素样式统一:所有可点击元素(按钮、标签、开关)均采用统一的样式规范——圆角、内边距、视觉反馈,让用户的交互体验更一致,跨端时可仅微调交互元素的尺寸与颜色,贴合鸿蒙端的设计规范。
所有RN原生基础组件均通过桥接层无缝映射为ArkUI组件,组件的Props传值、事件绑定、布局逻辑、列表渲染完全复用,无需修改任何核心代码,核心映射关系与适配要点如下:
| RN原生组件 | ArkUI映射组件 | 跨端复用要点 |
|---|---|---|
SafeAreaView |
SafeArea |
布局逻辑完全复用,仅需微调内边距贴合鸿蒙端的安全区域规范 |
FlatList |
List+ListItem |
桥接层自动实现数据渲染、keyExtractor、renderItem的映射,列表滚动逻辑完全复用 |
TouchableOpacity |
Button+Gesture |
onPress事件自动映射为onClick事件,点击透明度反馈由桥接层实现,保证跨端交互体验一致 |
Image |
Image |
Base64与网络图片加载逻辑完全复用,本地图片需调整路径,本次应用无本地图片,零适配成本 |
ScrollView |
ScrollView |
滚动逻辑、回弹效果完全复用,可通过桥接层开启鸿蒙端特有滑动手势 |
所有组件的核心Props(如flex/style/data/renderItem)均为跨端通用,无需修改,仅需对部分组件的次要Props(如elevation)进行轻量微调,贴合鸿蒙端的组件规范。
本次基于React Native+TypeScript实现的DataUsageMonitor数据使用监管助手,作为典型的工具类数据监控应用,全程践行了跨端友好型开发原则,其实现与适配解析提炼出工具类应用React Native鸿蒙跨端开发的核心最佳实践,能有效降低此类应用的鸿蒙适配成本,实现低成本、高效率、高还原的跨端落地,同时为工具类应用的RN开发提供了可复用的设计与实现思路。
真实演示案例代码:
// app.tsx
import React, { useState, useEffect } from 'react';
import { SafeAreaView, View, Text, StyleSheet, TouchableOpacity, ScrollView, Dimensions, Alert, FlatList, Image } from 'react-native';
// Base64 图标库
const ICONS_BASE64 = {
home: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
monitor: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
usage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
alert: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
settings: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
history: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
network: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
wifi: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
};
const { width } = Dimensions.get('window');
// 数据使用记录类型
type DataUsage = {
id: string;
appName: string;
packageName: string;
mobileData: number; // MB
wifiData: number; // MB
totalData: number; // MB
icon: string;
};
// 数据套餐类型
type DataPlan = {
id: string;
name: string;
total: number; // MB
used: number; // MB
remaining: number; // MB
resetDate: string;
};
// 警告类型
type Alert = {
id: string;
title: string;
message: string;
timestamp: string;
severity: 'low' | 'medium' | 'high';
};
const DataUsageMonitor = () => {
const [activeTab, setActiveTab] = useState<'home' | 'apps' | 'alerts' | 'settings'>('home');
const [dataUsage, setDataUsage] = useState<DataUsage[]>([
{ id: '1', appName: '微信', packageName: 'com.tencent.mm', mobileData: 120.5, wifiData: 340.2, totalData: 460.7, icon: '💬' },
{ id: '2', appName: '抖音', packageName: 'com.ss.android.ugc.aweme', mobileData: 85.3, wifiData: 210.8, totalData: 296.1, icon: '📱' },
{ id: '3', appName: '淘宝', packageName: 'com.taobao.taobao', mobileData: 45.2, wifiData: 120.5, totalData: 165.7, icon: '🛒' },
{ id: '4', appName: '腾讯视频', packageName: 'com.tencent.qqlive', mobileData: 32.1, wifiData: 85.6, totalData: 117.7, icon: '📺' },
{ id: '5', appName: '支付宝', packageName: 'com.eg.android.AlipayGphone', mobileData: 12.4, wifiData: 35.7, totalData: 48.1, icon: '💳' },
{ id: '6', appName: 'QQ音乐', packageName: 'com.tencent.qqmusic', mobileData: 8.7, wifiData: 42.3, totalData: 51.0, icon: '🎵' },
]);
const [dataPlan, setDataPlan] = useState<DataPlan>({
id: '1',
name: '月度套餐',
total: 30720, // 30GB
used: 12450, // 约12.16GB
remaining: 18270, // 约17.84GB
resetDate: '2023-06-01'
});
const [alerts, setAlerts] = useState<Alert[]>([
{ id: '1', title: '数据使用提醒', message: '您已使用套餐流量的75%', timestamp: '2023-05-15 14:30', severity: 'medium' },
{ id: '2', title: '后台应用警告', message: '微信后台使用了大量数据', timestamp: '2023-05-14 09:15', severity: 'low' },
{ id: '3', title: '数据用量超限', message: '移动数据已超过套餐限制', timestamp: '2023-05-12 18:45', severity: 'high' },
]);
const [settings, setSettings] = useState({
autoSync: true,
backgroundData: true,
warningThreshold: 80,
limitNotification: true
});
// 模拟实时数据更新
useEffect(() => {
const interval = setInterval(() => {
// 更新一些数据使用情况
setDataUsage(prev => prev.map(app => ({
...app,
mobileData: app.mobileData + Math.random() * 0.1,
wifiData: app.wifiData + Math.random() * 0.05,
totalData: app.totalData + Math.random() * 0.15
})));
// 更新套餐使用情况
setDataPlan(prev => {
const newDataUsed = prev.used + Math.random() * 0.5;
return {
...prev,
used: newDataUsed,
remaining: prev.total - newDataUsed
};
});
}, 5000); // 每5秒更新一次
return () => clearInterval(interval);
}, []);
// 计算百分比
const calculatePercentage = (used: number, total: number) => {
return Math.min(100, Math.round((used / total) * 100));
};
// 获取进度条颜色
const getProgressColor = (percentage: number) => {
if (percentage >= 90) return '#ef4444'; // 红色
if (percentage >= 75) return '#f59e0b'; // 黄色
return '#10b981'; // 绿色
};
// 渲染应用数据使用项
const renderAppItem = ({ item }: { item: DataUsage }) => (
<View style={styles.appItem}>
<View style={styles.appIcon}>{item.icon}</View>
<View style={styles.appInfo}>
<Text style={styles.appName}>{item.appName}</Text>
<Text style={styles.appPackage}>{item.packageName}</Text>
</View>
<View style={styles.appUsage}>
<Text style={styles.appMobile}>移动: {item.mobileData.toFixed(1)} MB</Text>
<Text style={styles.appWifi}>WiFi: {item.wifiData.toFixed(1)} MB</Text>
</View>
<View style={styles.appTotal}>
<Text style={styles.appTotalText}>{item.totalData.toFixed(1)} MB</Text>
</View>
</View>
);
// 渲染警告项
const renderAlertItem = ({ item }: { item: Alert }) => (
<View style={[styles.alertItem,
item.severity === 'high' && styles.alertHigh,
item.severity === 'medium' && styles.alertMedium,
item.severity === 'low' && styles.alertLow
]}>
<View style={styles.alertHeader}>
<Text style={styles.alertTitle}>{item.title}</Text>
<Text style={styles.alertTimestamp}>{item.timestamp}</Text>
</View>
<Text style={styles.alertMessage}>{item.message}</Text>
</View>
);
// 渲染主页内容
const renderHomeContent = () => (
<ScrollView style={styles.content}>
{/* 当前套餐状态 */}
<View style={styles.planCard}>
<Text style={styles.planName}>{dataPlan.name}</Text>
<Text style={styles.planUsage}>
已用: {dataPlan.used.toFixed(0)} MB / 总计: {dataPlan.total.toFixed(0)} MB
</Text>
<Text style={styles.planRemaining}>
剩余: {dataPlan.remaining.toFixed(0)} MB | 重置日期: {dataPlan.resetDate}
</Text>
<View style={styles.progressContainer}>
<View style={styles.progressBarBg}>
<View
style={[
styles.progressBar,
{
width: `${calculatePercentage(dataPlan.used, dataPlan.total)}%`,
backgroundColor: getProgressColor(calculatePercentage(dataPlan.used, dataPlan.total))
}
]}
/>
</View>
<Text style={styles.progressText}>
{calculatePercentage(dataPlan.used, dataPlan.total)}%
</Text>
</View>
</View>
{/* 快速操作按钮 */}
<View style={styles.quickActions}>
<TouchableOpacity style={styles.quickAction}>
<Text style={styles.quickActionIcon}>📊</Text>
<Text style={styles.quickActionText}>流量统计</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.quickAction}>
<Text style={styles.quickActionIcon}>📱</Text>
<Text style={styles.quickActionText}>应用管理</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.quickAction}>
<Text style={styles.quickActionIcon}>⚠️</Text>
<Text style={styles.quickActionText}>用量提醒</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.quickAction}>
<Text style={styles.quickActionIcon}>⚙️</Text>
<Text style={styles.quickActionText}>设置</Text>
</TouchableOpacity>
</View>
{/* 数据使用趋势 */}
<View style={styles.trendCard}>
<Text style={styles.sectionTitle}>本周数据使用趋势</Text>
<View style={styles.trendChart}>
{[60, 45, 75, 55, 80, 70, 65].map((value, index) => (
<View key={index} style={styles.trendColumn}>
<View
style={[
styles.trendBar,
{ height: `${value}%`, backgroundColor: value > 70 ? '#ef4444' : '#3b82f6' }
]}
/>
<Text style={styles.trendLabel}>{['一', '二', '三', '四', '五', '六', '日'][index]}</Text>
</View>
))}
</View>
</View>
{/* 最近警报 */}
<Text style={styles.sectionTitle}>最近警报</Text>
<FlatList
data={alerts.slice(0, 3)}
renderItem={renderAlertItem}
keyExtractor={item => item.id}
scrollEnabled={false}
/>
</ScrollView>
);
// 渲染应用数据页面
const renderAppsContent = () => (
<ScrollView style={styles.content}>
<Text style={styles.sectionTitle}>应用数据使用</Text>
<Text style={styles.appsSubtitle}>按数据使用量排序</Text>
<FlatList
data={[...dataUsage].sort((a, b) => b.totalData - a.totalData)}
renderItem={renderAppItem}
keyExtractor={item => item.id}
scrollEnabled={false}
/>
<View style={styles.infoCard}>
<Text style={styles.infoTitle}>使用说明</Text>
<Text style={styles.infoText}>• 点击应用名称可进入详细设置</Text>
<Text style={styles.infoText}>• 可限制后台数据使用</Text>
<Text style={styles.infoText}>• WiFi网络优先使用</Text>
</View>
</ScrollView>
);
// 渲染警报页面
const renderAlertsContent = () => (
<ScrollView style={styles.content}>
<Text style={styles.sectionTitle}>数据使用警报</Text>
<Text style={styles.alertsSubtitle}>您的数据使用情况提醒</Text>
<FlatList
data={alerts}
renderItem={renderAlertItem}
keyExtractor={item => item.id}
scrollEnabled={false}
/>
<View style={styles.infoCard}>
<Text style={styles.infoTitle}>警报说明</Text>
<Text style={styles.infoText}>• 高: 数据用量超过90%</Text>
<Text style={styles.infoText}>• 中: 数据用量超过75%</Text>
<Text style={styles.infoText}>• 低: 后台应用数据使用过多</Text>
</View>
</ScrollView>
);
// 渲染设置页面
const renderSettingsContent = () => (
<ScrollView style={styles.content}>
<Text style={styles.sectionTitle}>数据使用设置</Text>
<View style={styles.settingCard}>
<View style={styles.settingRow}>
<Text style={styles.settingLabel}>自动同步数据</Text>
<TouchableOpacity
style={[styles.toggle, settings.autoSync && styles.toggleActive]}
onPress={() => setSettings({...settings, autoSync: !settings.autoSync})}
>
<Text style={styles.toggleText}>{settings.autoSync ? '开' : '关'}</Text>
</TouchableOpacity>
</View>
<View style={styles.settingRow}>
<Text style={styles.settingLabel}>后台数据使用</Text>
<TouchableOpacity
style={[styles.toggle, settings.backgroundData && styles.toggleActive]}
onPress={() => setSettings({...settings, backgroundData: !settings.backgroundData})}
>
<Text style={styles.toggleText}>{settings.backgroundData ? '开' : '关'}</Text>
</TouchableOpacity>
</View>
<View style={styles.settingRow}>
<Text style={styles.settingLabel}>用量提醒阈值</Text>
<Text style={styles.settingValue}>{settings.warningThreshold}%</Text>
</View>
<View style={styles.settingRow}>
<Text style={styles.settingLabel}>超限通知</Text>
<TouchableOpacity
style={[styles.toggle, settings.limitNotification && styles.toggleActive]}
onPress={() => setSettings({...settings, limitNotification: !settings.limitNotification})}
>
<Text style={styles.toggleText}>{settings.limitNotification ? '开' : '关'}</Text>
</TouchableOpacity>
</View>
</View>
<TouchableOpacity style={styles.button} onPress={() => Alert.alert('重置数据', '是否重置所有数据使用统计?')}>
<Text style={styles.buttonText}>重置统计数据</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.buttonSecondary} onPress={() => Alert.alert('导出数据', '数据已导出到文件')}>
<Text style={styles.buttonSecondaryText}>导出使用报告</Text>
</TouchableOpacity>
</ScrollView>
);
return (
<SafeAreaView style={styles.container}>
{/* 头部 */}
<View style={styles.header}>
<Text style={styles.title}>数据使用监管助手</Text>
<TouchableOpacity onPress={() => Alert.alert('搜索', '搜索功能待实现')}>
<Text style={styles.searchIcon}>🔍</Text>
</TouchableOpacity>
</View>
{/* 内容区域 */}
{activeTab === 'home' && renderHomeContent()}
{activeTab === 'apps' && renderAppsContent()}
{activeTab === 'alerts' && renderAlertsContent()}
{activeTab === 'settings' && renderSettingsContent()}
{/* 底部导航 */}
<View style={styles.bottomNav}>
<TouchableOpacity
style={styles.navItem}
onPress={() => setActiveTab('home')}
>
<Text style={activeTab === 'home' ? styles.navIconActive : styles.navIcon}>🏠</Text>
<Text style={activeTab === 'home' ? styles.navTextActive : styles.navText}>首页</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.navItem}
onPress={() => setActiveTab('apps')}
>
<Text style={activeTab === 'apps' ? styles.navIconActive : styles.navIcon}>📱</Text>
<Text style={activeTab === 'apps' ? styles.navTextActive : styles.navText}>应用</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.navItem}
onPress={() => setActiveTab('alerts')}
>
<Text style={activeTab === 'alerts' ? styles.navIconActive : styles.navIcon}>⚠️</Text>
<Text style={activeTab === 'alerts' ? styles.navTextActive : styles.navText}>警报</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.navItem}
onPress={() => setActiveTab('settings')}
>
<Text style={activeTab === 'settings' ? styles.navIconActive : styles.navIcon}>⚙️</Text>
<Text style={activeTab === 'settings' ? styles.navTextActive : styles.navText}>设置</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f8fafc',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 20,
backgroundColor: '#ffffff',
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
title: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
},
searchIcon: {
fontSize: 20,
color: '#64748b',
},
content: {
flex: 1,
padding: 16,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1e293b',
marginVertical: 12,
},
appsSubtitle: {
fontSize: 14,
color: '#64748b',
marginBottom: 16,
},
alertsSubtitle: {
fontSize: 14,
color: '#64748b',
marginBottom: 16,
},
planCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 20,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
planName: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 8,
},
planUsage: {
fontSize: 14,
color: '#64748b',
marginBottom: 4,
},
planRemaining: {
fontSize: 14,
color: '#94a3b8',
marginBottom: 12,
},
progressContainer: {
flexDirection: 'row',
alignItems: 'center',
},
progressBarBg: {
flex: 1,
height: 12,
backgroundColor: '#e2e8f0',
borderRadius: 6,
overflow: 'hidden',
},
progressBar: {
height: '100%',
borderRadius: 6,
},
progressText: {
fontSize: 12,
color: '#64748b',
marginLeft: 8,
minWidth: 30,
},
quickActions: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 20,
},
quickAction: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
alignItems: 'center',
width: width / 4.5,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
quickActionIcon: {
fontSize: 24,
marginBottom: 8,
},
quickActionText: {
fontSize: 12,
color: '#1e293b',
textAlign: 'center',
},
trendCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 20,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
trendChart: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'flex-end',
height: 100,
marginTop: 10,
},
trendColumn: {
alignItems: 'center',
flex: 1,
},
trendBar: {
width: 20,
borderRadius: 4,
marginBottom: 8,
},
trendLabel: {
fontSize: 12,
color: '#64748b',
},
appItem: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
flexDirection: 'row',
alignItems: 'center',
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
appIcon: {
fontSize: 24,
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: '#dbeafe',
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
appInfo: {
flex: 1,
},
appName: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
},
appPackage: {
fontSize: 12,
color: '#94a3b8',
},
appUsage: {
alignItems: 'flex-end',
marginRight: 8,
},
appMobile: {
fontSize: 12,
color: '#64748b',
},
appWifi: {
fontSize: 12,
color: '#64748b',
},
appTotal: {
alignItems: 'center',
justifyContent: 'center',
minWidth: 60,
},
appTotalText: {
fontSize: 14,
fontWeight: 'bold',
color: '#1e293b',
},
alertItem: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 12,
elevation: 1,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
alertHigh: {
borderLeftWidth: 4,
borderLeftColor: '#ef4444',
},
alertMedium: {
borderLeftWidth: 4,
borderLeftColor: '#f59e0b',
},
alertLow: {
borderLeftWidth: 4,
borderLeftColor: '#10b981',
},
alertHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 4,
},
alertTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
},
alertTimestamp: {
fontSize: 12,
color: '#94a3b8',
},
alertMessage: {
fontSize: 14,
color: '#64748b',
},
infoCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginTop: 20,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
infoTitle: {
fontSize: 16,
fontWeight: 'bold',
color: '#1e293b',
marginBottom: 8,
},
infoText: {
fontSize: 14,
color: '#64748b',
marginBottom: 4,
},
settingCard: {
backgroundColor: '#ffffff',
borderRadius: 12,
padding: 16,
marginBottom: 16,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
settingRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 12,
borderBottomWidth: 1,
borderBottomColor: '#e2e8f0',
},
settingLabel: {
fontSize: 16,
color: '#1e293b',
},
settingValue: {
fontSize: 16,
color: '#64748b',
},
toggle: {
width: 50,
height: 24,
borderRadius: 12,
backgroundColor: '#cbd5e1',
justifyContent: 'center',
alignItems: 'center',
},
toggleActive: {
backgroundColor: '#3b82f6',
},
toggleText: {
fontSize: 12,
color: '#ffffff',
fontWeight: 'bold',
},
button: {
backgroundColor: '#3b82f6',
padding: 16,
borderRadius: 8,
alignItems: 'center',
marginBottom: 12,
},
buttonText: {
color: '#ffffff',
fontWeight: 'bold',
fontSize: 16,
},
buttonSecondary: {
backgroundColor: '#f1f5f9',
padding: 16,
borderRadius: 8,
alignItems: 'center',
},
buttonSecondaryText: {
color: '#3b82f6',
fontWeight: 'bold',
fontSize: 16,
},
bottomNav: {
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#ffffff',
borderTopWidth: 1,
borderTopColor: '#e2e8f0',
paddingVertical: 12,
},
navItem: {
alignItems: 'center',
flex: 1,
},
navIcon: {
fontSize: 20,
color: '#94a3b8',
marginBottom: 4,
},
navIconActive: {
fontSize: 20,
color: '#3b82f6',
marginBottom: 4,
},
navText: {
fontSize: 12,
color: '#94a3b8',
},
navTextActive: {
fontSize: 12,
color: '#3b82f6',
fontWeight: '500',
},
});
export default DataUsageMonitor;

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

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

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

本文介绍了一款基于React Native开发的跨平台流量监控应用DataUsageMonitor。该应用采用函数式组件架构,通过useState和useEffect实现轻量级状态管理,支持流量套餐监控、应用级统计、用量警报和设置管理等核心功能。
技术亮点包括:
- 使用TypeScript接口确保数据类型安全
- 通过useEffect模拟实时数据更新
- 动态进度条颜色反馈流量使用状态
- 模块化设计实现代码复用
在跨端开发方面,应用严格遵循React Native与鸿蒙兼容规范:
- 使用基础组件和通用API
- Base64格式图标资源
- Flex弹性布局
- 纯JS/TS业务逻辑
该设计可作为工具类数据监控应用的开发范本,其模块化架构和跨端友好特性显著降低了鸿蒙平台的适配成本。
欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。
更多推荐




所有评论(0)