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 安装

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

npm install @rneui/themed@4.0.0-rc.8
npm install @rneui/base@4.0.0-rc.7

2. 验证安装

安装完成后,检查 package.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 配置中添加字体映射(根据实际下载的字体文件):

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

# 添加 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

#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

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 的各种常用组件:

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 按钮组件

<Button title="Primary" />
<Button title="Secondary" type="outline" />
<Button title="Clear" type="clear" />

Button 组件支持多种类型:

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

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

2. Avatar 头像组件

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

Avatar 组件支持:

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

3. Input 输入框组件

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

Input 组件支持:

  • 左右图标
  • 错误提示
  • 占位符
  • 多种输入类型

4. SearchBar 搜索栏组件

<SearchBar
  placeholder="Type Here..."
  onChangeText={(text) => setValue(text)}
  value={value}
  round
/>

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

5. Slider 滑块组件

<Slider
  value={sliderValue}
  onValueChange={setSliderValue}
  maximumValue={1}
  minimumValue={0}
  step={0.1}
/>

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

6. CheckBox 复选框组件

<CheckBox
  title="Click Here"
  checked={checked}
  onPress={() => setChecked(!checked)}
/>

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

7. ListItem 列表项组件

<ListItem bottomDivider>
  <Icon name={l} />
  <ListItem.Content>
    <ListItem.Title>{l}</ListItem.Title>
  </ListItem.Content>
  <ListItem.Chevron />
</ListItem>

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

8. Icon 图标组件

<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 主题提供者

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

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

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

⚠️ 注意事项与最佳实践

1. 主题配置

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

2. 组件导入

// 带主题的组件
import { Button, Icon } from '@rneui/themed';

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

3. 图标库支持

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

npm install @rneui/base @rneui/themed

4. 性能优化

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

5. 样式定制

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

<Button
  title="Custom Style"
  style={styles.buttonStyle}
  containerStyle={styles.containerStyle}
/>

6. HarmonyOS 兼容性

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

🧪 测试验证

1. Android 平台测试

npm run android

测试要点:

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

2. iOS 平台测试

npm run ios

测试要点:

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

3. HarmonyOS 平台测试

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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐