ReactNative for Harmony项目鸿蒙化三方库集成实战:react-native-elements

RN项目鸿蒙化三方库集成实战:react-native-elements

📋 前言

React Native Elements 是一个跨平台的 React Native UI 组件库,提供了一套完整且美观的 UI 组件。它遵循 React Native 的设计原则,支持主题定制,并且兼容 Android、iOS 和 HarmonyOS 三端。使用 React Native Elements 可以大大提升开发效率,构建出美观一致的用户界面。

🎯 库简介

基本信息

  • 库名称: @rneui/themed 和 @rneui/base
  • 当前版本: 4.0.0-rc.8 (@rneui/themed) / 4.0.0-rc.7 (@rneui/base)
  • 官方仓库: https://github.com/react-native-elements/react-native-elements
  • 主要功能 :
    • 提供完整的 UI 组件库(Button、Card、List、Avatar、Input 等)
    • 支持主题定制
    • 跨平台一致的设计风格
    • 完全兼容 Android、iOS 和 HarmonyOS

为什么需要这个库?

  • 提高开发效率: 提供开箱即用的精美组件
  • 设计一致性: 统一的视觉风格和交互体验
  • 主题定制: 支持全局主题和组件级主题
  • 跨平台兼容: 在三端提供一致的体验
  • 社区支持: 活跃的社区和完善的文档

📦 安装步骤

1. 使用 npm 安装

在项目根目录执行以下命令:

bash 复制代码
npm install @rneui/themed@4.0.0-rc.8
npm install @rneui/base@4.0.0-rc.7

2. 验证安装

安装完成后,检查 package.json 文件,应该能看到新增的依赖:

json 复制代码
{
  "dependencies": {
    "@rneui/themed": "4.0.0-rc.8",
    "@rneui/base": "4.0.0-rc.7",
    // ... 其他依赖
  }
}

🔧 HarmonyOS 平台配置

1. 依赖库配置

react-native-elements HarmonyOS 侧实现依赖以下库的原生端代码:

  • @react-native-oh-tpl/react-native-safe-area-context
  • @react-native-oh-tpl/react-native-linear-gradient

如已在 HarmonyOS 工程中引入过这些库,则无需再次引入。如未引入,请参考对应文档的 Link 章节进行引入。

不懂得参考我的另一篇文章:适配react-native-picker

还有:官方适配说明

2. 配置字体文件(可选)

如果需要使用 react-native-elements 的图标功能,需要配置字体文件。

步骤 1: 获取字体文件

从 react-native-vector-icons 的 GitHub 仓库下载字体文件:

https://github.com/oblador/react-native-vector-icons/tree/master/Fonts

下载所需的字体文件(如 FontAwesome.ttf、MaterialIcons.ttf 等)。

步骤 2: 复制字体文件

将下载的字体文件复制到 entry/src/main/resources/rawfile/fonts 目录下。

步骤 3: 配置字体注册

打开 entry/src/main/ets/pages/Index.ets,在 RNApp 配置中添加字体映射(根据实际下载的字体文件):

typescript 复制代码
RNApp({
  rnInstanceConfig: {
    fontResourceByFontFamily: {
      // 根据实际下载的字体文件配置,例如:
      // 'FontAwesome': $rawfile('fonts/FontAwesome.ttf'),
      // 'MaterialIcons': $rawfile('fonts/MaterialIcons.ttf'),
    }
  }
})

注意:如果项目中已经配置过 react-native-vector-icons 的字体,可以跳过此步骤。

3. 配置 CMakeLists

如果未引入 react-native-safe-area-context 或 react-native-linear-gradient,需要配置:

修改 entry/src/main/cpp/CMakeLists.txt

cmake 复制代码
# 添加 react-native-safe-area-context
add_subdirectory("${OH_MODULES}/@react-native-oh-tpl/react-native-safe-area-context/src/main/cpp" ./safe-area)
target_link_libraries(rnoh_app PUBLIC rnoh_safe_area)

# 添加 react-native-linear-gradient
add_subdirectory("${OH_MODULES}/@react-native-oh-tpl/react-native-linear-gradient/src/main/cpp" ./linear-gradient)
target_link_libraries(rnoh_app PUBLIC rnoh_linear_gradient)

4. 配置 PackageProvider

修改 entry/src/main/cpp/PackageProvider.cpp

cpp 复制代码
#include "SafeAreaContextPackage.h"
#include "LinearGradientPackage.h"

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
        std::make_shared<RNOHGeneratedPackage>(ctx),
        std::make_shared<SafeAreaContextPackage>(ctx),
        std::make_shared<LinearGradientPackage>(ctx),
    };
}

5. 配置 RNPackagesFactory

修改 entry/src/main/ets/RNPackagesFactory.ts

typescript 复制代码
import { SafeAreaViewPackage } from '@react-native-oh-tpl/react-native-safe-area-context/ts';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new SafeAreaViewPackage(ctx),
  ];

💻 完整代码示例

下面是一个完整的示例,展示了 react-native-elements 的各种常用组件:

typescript 复制代码
import React, { useState } from 'react';
import {
  SafeAreaView,
  ScrollView,
  StyleSheet,
  View,
  Alert,
} from 'react-native';
import {
  Avatar,
  Button,
  Card,
  Icon,
  Input,
  ListItem,
  SearchBar,
  Slider,
  Text,
  CheckBox,
  ThemeProvider,
  createTheme,
} from '@rneui/themed';

// 创建自定义主题
const theme = createTheme({
  lightColors: {
    primary: '#2089dc',
    secondary: '#3ac4fa',
    grey0: '#f7f7f7',
    grey1: '#ededed',
    grey2: '#dadada',
    grey3: '#b7b7b7',
    grey4: '#6e6e6e',
    grey5: '#3b3b3b',
  },
  darkColors: {
    primary: '#2089dc',
    secondary: '#3ac4fa',
    grey0: '#2c2c2c',
    grey1: '#383838',
    grey2: '#464646',
    grey3: '#666666',
    grey4: '#999999',
    grey5: '#d9d9d9',
  },
  mode: 'light',
});

function ElementsDemo() {
  const [value, setValue] = useState('');
  const [sliderValue, setSliderValue] = useState(0.5);
  const [checked, setChecked] = useState(false);
  const [selectedIndex, setSelectedIndex] = useState(0);

  return (
    <ThemeProvider theme={theme}>
      <SafeAreaView style={styles.container}>
        <ScrollView>
          {/* 1. Button 按钮 */}
          <Card containerStyle={styles.card}>
            <Card.Title>Button 按钮</Card.Title>
            <Card.Divider />
            <View style={styles.buttonContainer}>
              <Button title="Primary" />
              <Button title="Secondary" type="outline" />
              <Button title="Clear" type="clear" />
            </View>
            <View style={styles.buttonContainer}>
              <Button
                title="With Icon"
                icon={<Icon name="home" color="#ffffff" />}
                iconRight
              />
              <Button
                title="Loading"
                loading
              />
            </View>
          </Card>

          {/* 2. Avatar 头像 */}
          <Card containerStyle={styles.card}>
            <Card.Title>Avatar 头像</Card.Title>
            <Card.Divider />
            <View style={styles.avatarContainer}>
              <Avatar
                size="large"
                rounded
                source={{ uri: 'https://randomuser.me/api/portraits/men/36.jpg' }}
              />
              <Avatar
                size="large"
                rounded
                title="JD"
                containerStyle={{ backgroundColor: '#2089dc' }}
              />
              <Avatar
                size="large"
                rounded
                icon={{ name: 'user', type: 'font-awesome' }}
                containerStyle={{ backgroundColor: '#2089dc' }}
              />
            </View>
          </Card>

          {/* 3. Input 输入框 */}
          <Card containerStyle={styles.card}>
            <Card.Title>Input 输入框</Card.Title>
            <Card.Divider />
            <Input
              placeholder="Basic input"
              onChangeText={(text) => setValue(text)}
            />
            <Input
              placeholder="With icon"
              leftIcon={{ type: 'font-awesome', name: 'user' }}
            />
            <Input
              placeholder="Error"
              errorStyle={{ color: 'red' }}
              errorMessage="ENTER A VALID ERROR HERE"
            />
          </Card>

          {/* 4. SearchBar 搜索栏 */}
          <Card containerStyle={styles.card}>
            <Card.Title>SearchBar 搜索栏</Card.Title>
            <Card.Divider />
            <SearchBar
              placeholder="Type Here..."
              onChangeText={(text) => setValue(text)}
              value={value}
              round
            />
          </Card>

          {/* 5. Slider 滑块 */}
          <Card containerStyle={styles.card}>
            <Card.Title>Slider 滑块</Card.Title>
            <Card.Divider />
            <Slider
              value={sliderValue}
              onValueChange={setSliderValue}
              maximumValue={1}
              minimumValue={0}
              step={0.1}
              allowTouchTrack
              trackStyle={{ height: 5, backgroundColor: 'transparent' }}
              thumbStyle={{ height: 20, width: 20, backgroundColor: 'transparent' }}
              thumbProps={{
                children: (
                  <Icon
                    name="circle"
                    type="font-awesome"
                    size={20}
                    reverse
                    color="#2089dc"
                  />
                ),
              }}
            />
            <Text>Value: {sliderValue.toFixed(1)}</Text>
          </Card>

          {/* 6. CheckBox 复选框 */}
          <Card containerStyle={styles.card}>
            <Card.Title>CheckBox 复选框</Card.Title>
            <Card.Divider />
            <CheckBox
              title="Click Here"
              checked={checked}
              onPress={() => setChecked(!checked)}
            />
            <CheckBox
              center
              title="Click Here"
              checked={checked}
              onPress={() => setChecked(!checked)}
            />
            <CheckBox
              center
              title="Click Here"
              checkedIcon={<Icon name="checkbox" type="font-awesome" size={25} color="#2089dc" />}
              uncheckedIcon={<Icon name="square-o" type="font-awesome" size={25} color="#2089dc" />}
              checked={checked}
              onPress={() => setChecked(!checked)}
            />
          </Card>

          {/* 7. ListItem 列表项 */}
          <Card containerStyle={styles.card}>
            <Card.Title>ListItem 列表项</Card.Title>
            <Card.Divider />
            <View>
              {[
                'Appointments',
                'Trips',
                'Favorites',
                'Settings',
              ].map((l, i) => (
                <ListItem key={i} bottomDivider>
                  <Icon name={l} />
                  <ListItem.Content>
                    <ListItem.Title>{l}</ListItem.Title>
                  </ListItem.Content>
                  <ListItem.Chevron />
                </ListItem>
              ))}
            </View>
          </Card>

          {/* 8. Icon 图标 */}
          <Card containerStyle={styles.card}>
            <Card.Title>Icon 图标</Card.Title>
            <Card.Divider />
            <View style={styles.iconContainer}>
              <Icon name="home" type="font-awesome" size={30} />
              <Icon name="heart" type="font-awesome" size={30} color="#e74c3c" />
              <Icon name="star" type="font-awesome" size={30} color="#f1c40f" />
              <Icon name="check-circle" type="font-awesome" size={30} color="#2ecc71" />
            </View>
          </Card>

        </ScrollView>
      </SafeAreaView>
    </ThemeProvider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f7f7f7',
  },
  card: {
    marginBottom: 15,
  },
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    marginBottom: 15,
  },
  avatarContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    paddingVertical: 15,
  },
  iconContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    paddingVertical: 15,
  },
});

export default ElementsDemo;

💻 代码讲解

1. Button 按钮组件

typescript 复制代码
<Button title="Primary" />
<Button title="Secondary" type="outline" />
<Button title="Clear" type="clear" />

Button 组件支持多种类型:

  • solid (默认): 实心按钮
  • outline: 轮廓按钮
  • clear: 透明按钮

可以添加图标、加载状态等。

2. Avatar 头像组件

typescript 复制代码
<Avatar
  size="large"
  rounded
  source={{ uri: 'https://randomuser.me/api/portraits/men/36.jpg' }}
/>

Avatar 组件支持:

  • 图片头像
  • 文字头像
  • 图标头像
  • 圆形/方形
  • 不同尺寸(small, medium, large, xlarge)

3. Input 输入框组件

typescript 复制代码
<Input
  placeholder="With icon"
  leftIcon={{ type: 'font-awesome', name: 'user' }}
/>

Input 组件支持:

  • 左右图标
  • 错误提示
  • 占位符
  • 多种输入类型
typescript 复制代码
<SearchBar
  placeholder="Type Here..."
  onChangeText={(text) => setValue(text)}
  value={value}
  round
/>

SearchBar 支持圆形样式、清除按钮等。

5. Slider 滑块组件

typescript 复制代码
<Slider
  value={sliderValue}
  onValueChange={setSliderValue}
  maximumValue={1}
  minimumValue={0}
  step={0.1}
/>

Slider 组件支持自定义滑块样式和图标。

6. CheckBox 复选框组件

typescript 复制代码
<CheckBox
  title="Click Here"
  checked={checked}
  onPress={() => setChecked(!checked)}
/>

CheckBox 支持自定义图标和样式。

7. ListItem 列表项组件

typescript 复制代码
<ListItem bottomDivider>
  <Icon name={l} />
  <ListItem.Content>
    <ListItem.Title>{l}</ListItem.Title>
  </ListItem.Content>
  <ListItem.Chevron />
</ListItem>

ListItem 是构建列表的基础组件,支持左图标、右箭头等。

8. Icon 图标组件

typescript 复制代码
<Icon name="home" type="font-awesome" size={30} />

Icon 组件支持多种图标库:

  • font-awesome
  • material
  • material-community
  • ionicons
  • octicons
  • zocial
  • simple-line-icon
  • feather
  • antdesign
  • entypo
  • evilicons

9. ThemeProvider 主题提供者

typescript 复制代码
const theme = createTheme({
  lightColors: {
    primary: '#2089dc',
    secondary: '#3ac4fa',
  },
  mode: 'light',
});

<ThemeProvider theme={theme}>
  {/* 你的应用组件 */}
</ThemeProvider>

ThemeProvider 允许你创建和应用自定义主题,支持亮色和暗色模式。

⚠️ 注意事项与最佳实践

1. 主题配置

  • 在应用最外层使用 ThemeProvider 包裹
  • 使用 createTheme 创建主题对象
  • 支持 lightColors 和 darkColors 两套配色方案

2. 组件导入

typescript 复制代码
// 带主题的组件
import { Button, Icon } from '@rneui/themed';

// 基础组件
import { Text } from '@rneui/base';

3. 图标库支持

react-native-elements 支持多种图标库,需要安装对应的依赖:

bash 复制代码
npm install @rneui/base @rneui/themed

4. 性能优化

  • 对于大量列表,使用 React Native 的 FlatList 而不是手动渲染多个 ListItem
  • 避免在 render 方法中创建新的样式对象
  • 使用 React.memo 优化组件性能

5. 样式定制

每个组件都支持 style 和 containerStyle 属性:

typescript 复制代码
<Button
  title="Custom Style"
  style={styles.buttonStyle}
  containerStyle={styles.containerStyle}
/>

6. HarmonyOS 兼容性

react-native-elements 在 HarmonyOS 上完全兼容,无需额外配置。所有组件在三端都能正常工作。

🧪 测试验证

1. Android 平台测试

bash 复制代码
npm run android

测试要点:

  • 检查所有组件是否正常渲染
  • 验证主题颜色是否正确
  • 测试交互功能(点击、滑动等)

2. iOS 平台测试

bash 复制代码
npm run ios

测试要点:

  • 检查组件样式是否一致
  • 验证动画效果
  • 测试不同屏幕尺寸

3. HarmonyOS 平台测试

bash 复制代码
npm run harmony

测试要点:

  • 验证组件渲染
  • 检查主题应用
  • 测试交互响应

4. 常见问题排查

问题 1: 图标不显示

  • 确保已安装对应的图标库
  • 检查图标名称和类型是否正确

问题 2: 主题不生效

  • 确保使用 ThemeProvider 包裹应用
  • 检查主题配置是否正确

问题 3: 样式不一致

  • 检查各平台的样式属性支持情况
  • 使用 Platform API 进行平台特定样式处理

📊 对比:原生组件 vs react-native-elements

特性 原生组件 react-native-elements
开箱即用
设计一致性 ⚠️ 需要自己设计 ✅ 统一设计
主题支持
图标集成 ✅ 多种图标库
文档完善 ⚠️ ✅ 详尽文档
社区支持 ⚠️ ✅ 活跃社区
性能 ⚠️ 稍有损耗
定制性 ✅ 完全控制 ⚠️ 受限于API

📝 总结

通过集成 react-native-elements,我们为项目添加了一套完整的 UI 组件库。这个库提供了丰富的组件、统一的主题系统和跨平台的一致性,可以大大提升开发效率和用户体验。

关键要点回顾

  • 安装依赖 : npm install @rneui/themed @rneui/base
  • 配置平台: 纯 JavaScript 库,无需手动配置原生代码
  • 集成代码: 使用 ThemeProvider 和各种组件
  • 主题定制: 使用 createTheme 创建自定义主题
  • 测试验证: 确保三端表现一致

实际效果

  • Android: 显示 Material Design 风格的组件
  • iOS: 显示 iOS 风格的组件
  • HarmonyOS: 显示 HarmonyOS 风格的组件

希望这篇教程能帮助你顺利集成 react-native-elements,提升应用的用户体验!


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

相关推荐
killerbasd4 小时前
还是迷茫 5.3
前端·react.js·前端框架
李李李勃谦6 小时前
鸿蒙PC密码管理器实战:本地加密存储与自动填充完整实现
华为·harmonyos
Swift社区7 小时前
鸿蒙 App 架构中的“领域拆分”
华为·架构·harmonyos
江南十四行10 小时前
ReAct Agent 基本理论与项目实战(一)
前端·react.js·前端框架
maaath10 小时前
【maaath】Flutter for OpenHarmony 手表配饰应用实战开发
flutter·华为·harmonyos
maaath11 小时前
【maaath】Flutter for OpenHarmony 跨平台计算器应用开发实践
flutter·华为·harmonyos
谢尔登14 小时前
10_从 React Hooks 本质看 useState
前端·ubuntu·react.js
辰同学ovo14 小时前
从全局登录状态管理学习 Redux
前端·javascript·学习·react.js
光影少年14 小时前
reeact虚拟DOM、Diff算法原理、key的作用与为什么不能用index
前端·react.js·掘金·金石计划
江南十四行15 小时前
ReAct Agent 基本理论与项目实战(二)
前端·react.js·前端框架