📋 前言

选择器(Picker)是移动应用中非常常见的交互组件,用于让用户从预定义的选项列表中进行选择。@react-native-ohos/react-native-picker 是一个专为 React Native 跨平台应用(包括 HarmonyOS)设计的选择器库,它采用底部弹出式选择器,支持单列、多列、联动等多种选择场景。

🎯 库简介

基本信息

  • 库名称: @react-native-ohos/react-native-picker

  • 版本信息:

    • 4.3.10: 支持 RN 0.72 版本
    • 4.4.0: 支持 RN 0.77 版本
  • 官方仓库: https://github.com/react-native-oh-library/react-native-picker

  • 主要功能:

    • 支持单列、多列、联动选择器
    • 丰富的自定义样式配置
    • 兼容 Android、iOS 和 HarmonyOS 三端
  • 兼容性验证:

    • RNOH: 0.72.96; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112
    • RNOH: 0.72.33; SDK: HarmonyOS NEXT B1; IDE: DevEco Studio: 5.0.3.900; ROM: Next.0.0.71
    • RNOH: 0.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112

为什么需要这个库?

虽然各平台都有原生的选择器组件,但使用 @react-native-ohos/react-native-picker 有以下优势:

  • 统一API: 在三端使用相同的API,降低开发成本
  • 底部弹出模式: 提供统一的底部弹出式选择器体验
  • 支持联动选择: 支持多级联动选择,如省市区选择
  • HarmonyOS 支持: 专门为 HarmonyOS 平台适配
  • 样式灵活: 支持丰富的自定义样式配置

📦 安装步骤

1. 使用 npm 安装

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

npm install @react-native-ohos/react-native-picker

2. 验证安装

安装完成后,检查 package.json 文件,应该能看到新增的依赖。根据您的 RN 版本选择对应的库版本:

{
  "dependencies": {
    "@react-native-ohos/react-native-picker": "4.4.0", // RN 0.77 版本
    // 或
    "@react-native-ohos/react-native-picker": "4.3.10", // RN 0.72 版本
    // ... 其他依赖
  }
}

我的安装后如下

"@react-native-ohos/react-native-picker": "^4.3.8-rc.1"

🔧 HarmonyOS 平台配置 ⭐

由于 HarmonyOS 暂不支持 AutoLink,需要手动配置原生端代码。本文采用方法二:直接链接源码的方式。

1 引入原生端代码

方法二:直接链接源码

目前 DevEco Studio 不支持通过源码引入外部 module,我们推荐使用直接链接源码的方式,将源码通过操作改成 harmony 工程的内部模块。

步骤 1: 把 <RN工程>/node_modules/@react-native-ohos/react-native-picker/harmony 目录下的源码 picker 复制到 harmony(鸿蒙壳工程)工程根目录下。
在这里插入图片描述

在这里插入图片描述

步骤 2: 在 harmony 工程根目录的 build-profile.template.json5(若存在)和 build-profile.json5 添加以下模块:

modules: [
  ...
  {
    name: '<xxx>',
    srcPath: './<xxx>',
  },
  {
    name: 'picker',
    srcPath: './picker',
  }
]

在这里插入图片描述

步骤 3: 打开 picker/oh-package.json5,修改 react-native-openharmony 和项目的版本一致。
在这里插入图片描述

步骤 4: 打开 entry/oh-package.json5,添加以下依赖:

"dependencies": {
  "@rnoh/react-native-openharmony": "0.72.90",
  "@react-native-ohos/react-native-picker": "file:../picker"
}

在这里插入图片描述

步骤 5: 点击 DevEco Studio 右上角的 sync 按钮

2 配置CMakeLists和导入PickerPackage

  • 修改 entry/src/main/cpp/CMakeLists.txt
set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-picker/src/main/cpp" ./picker)
target_link_libraries(rnoh_app PUBLIC rnoh_native_picker)
  • 修改 entry/src/main/cpp/PackageProvider.cpp
#include "PickerPackage.h"

std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
        // ... 其他包
        std::make_shared<PickerPackage>(ctx),
    };
}

3 在ArkTs侧引入PickerViewPackage

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

import { PickerViewPackage } from '@react-native-ohos/react-native-picker/ts';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    // ... 其他包
    new PickerViewPackage(ctx),
  ];
}

注意

picker/ts.ts修改为picker/ts.ets否则在运行的时候会报错

在这里插入图片描述

💻 完整代码示例

下面是一个完整的示例,展示了 react-native-picker 的各种使用场景,包括单列选择、多列选择、联动选择等:

import React, { useState } from 'react';
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, SafeAreaView } from 'react-native';
import Picker from 'react-native-picker';

function PickerDemo() {
  // 状态管理
  const [selectedLanguage, setSelectedLanguage] = useState('JavaScript');
  const [selectedColor, setSelectedColor] = useState('红色');
  const [selectedCity, setSelectedCity] = useState('北京');
  const [selectedDate, setSelectedDate] = useState('2024-01-01');
  const [selectedSpec, setSelectedSpec] = useState({ color: '黑色', size: 'M', quantity: '1' });

  // 1. 单列选择器 - 选择编程语言
  const showLanguagePicker = () => {
    Picker.init({
      pickerData: ['Java', 'JavaScript', 'Python', 'C++', 'C#'],
      selectedValue: [selectedLanguage],
      pickerTitleText: '选择编程语言',
      pickerConfirmBtnText: '确定',
      pickerCancelBtnText: '取消',
      onPickerConfirm: (pickedValue) => {
        const value = Array.isArray(pickedValue) ? pickedValue[0] : pickedValue;
        setSelectedLanguage(value);
      },
    });
    Picker.show();
  };

  // 2. 单列选择器 - 自定义样式选择颜色
  const showColorPicker = () => {
    Picker.init({
      pickerData: ['红色', '绿色', '蓝色', '黄色', '紫色'],
      selectedValue: [selectedColor],
      pickerTitleText: '选择颜色',
      pickerConfirmBtnText: '确定',
      pickerCancelBtnText: '取消',
      // 自定义样式
      pickerToolBarBg: [255, 255, 255, 1],
      pickerBg: [255, 255, 255, 1],
      pickerTitleColor: [0, 0, 0, 1],
      pickerConfirmBtnColor: [0, 122, 255, 1],
      pickerCancelBtnColor: [0, 0, 0, 1],
      pickerFontColor: [0, 0, 0, 1],
      pickerFontSize: 16,
      onPickerConfirm: (pickedValue) => {
        const value = Array.isArray(pickedValue) ? pickedValue[0] : pickedValue;
        setSelectedColor(value);
      },
    });
    Picker.show();
  };

  // 3. 单列选择器 - 城市选择
  const showCityPicker = () => {
    Picker.init({
      pickerData: ['北京', '上海', '广州', '深圳', '杭州'],
      selectedValue: [selectedCity],
      pickerTitleText: '选择城市',
      onPickerConfirm: (pickedValue) => {
        const value = Array.isArray(pickedValue) ? pickedValue[0] : pickedValue;
        setSelectedCity(value);
      },
    });
    Picker.show();
  };

  // 4. 多列选择器 - 日期选择
  const showDatePicker = () => {
    const years = Array.from({ length: 100 }, (_, i) => `${2024 - i}`);
    const months = Array.from({ length: 12 }, (_, i) => `${i + 1}`);
    const days = Array.from({ length: 31 }, (_, i) => `${i + 1}`);

    Picker.init({
      pickerData: [years, months, days],
      selectedValue: ['2024年', '1月', '1日'],
      pickerTitleText: '选择日期',
      onPickerConfirm: (pickedValue) => {
        const values = Array.isArray(pickedValue) ? pickedValue : [pickedValue];
        const year = values[0] ? values[0].replace('年', '') : '2024';
        const month = values[1] ? values[1].replace('月', '').padStart(2, '0') : '01';
        const day = values[2] ? values[2].replace('日', '').padStart(2, '0') : '01';
        setSelectedDate(`${year}-${month}-${day}`);
      },
    });
    Picker.show();
  };

  // 5. 联动选择器 - 商品规格
  const showSpecPicker = () => {
    const colorData = ['黑色', '白色', '灰色', '蓝色'];
    const sizeData = ['XS', 'S', 'M', 'L', 'XL', 'XXL'];
    const quantityData = Array.from({ length: 10 }, (_, i) => `${i + 1}`);

    Picker.init({
      pickerData: [colorData, sizeData, quantityData],
      selectedValue: [selectedSpec.color, selectedSpec.size, selectedSpec.quantity],
      pickerTitleText: '选择商品规格',
      onPickerConfirm: (pickedValue) => {
        const values = Array.isArray(pickedValue) ? pickedValue : [pickedValue];
        setSelectedSpec({
          color: values[0] || selectedSpec.color,
          size: values[1] || selectedSpec.size,
          quantity: values[2] || selectedSpec.quantity,
        });
      },
    });
    Picker.show();
  };

  // 6. 高级联动选择器 - 省市区三级联动
  const showAreaPicker = () => {
    const data: Record<string, string[]> = {
      北京市: ['东城区', '西城区', '朝阳区', '海淀区'],
      上海市: ['黄浦区', '徐汇区', '长宁区', '静安区'],
      广东省: ['广州市', '深圳市', '珠海市', '佛山市'],
    };

    const provinces = Object.keys(data);
    const cities = data[provinces[0] as keyof typeof data];

    Picker.init({
      pickerData: [provinces, cities],
      selectedValue: [provinces[0], cities[0]],
      pickerTitleText: '选择省市区',
      onPickerConfirm: (pickedValue) => {
        console.log('选择结果:', pickedValue);
      },
    });
    Picker.show();
  };

  // 颜色映射
  const colorMap: Record<string, string> = {
    '红色': '#FF0000',
    '绿色': '#00FF00',
    '蓝色': '#0000FF',
    '黄色': '#FFFF00',
    '紫色': '#800080',
  };

  return (
    <SafeAreaView style={styles.container}>
      <ScrollView style={styles.scrollView} contentContainerStyle={styles.scrollContent}>
        <Text style={styles.title}>Picker 选择器演示</Text>

        {/* 1. 基础单列选择器 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>1. 基础单列选择器</Text>
          <Text style={styles.description}>选择编程语言,展示最基础的使用方式</Text>
          <TouchableOpacity style={styles.button} onPress={showLanguagePicker}>
            <Text style={styles.buttonText}>打开选择器</Text>
          </TouchableOpacity>
          <Text style={styles.result}>当前选择: {selectedLanguage}</Text>
        </View>

        {/* 2. 自定义样式选择器 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>2. 自定义样式选择器</Text>
          <Text style={styles.description}>自定义颜色、字体大小等样式配置</Text>
          <TouchableOpacity style={styles.button} onPress={showColorPicker}>
            <Text style={styles.buttonText}>打开选择器</Text>
          </TouchableOpacity>
          <View style={styles.colorPreviewContainer}>
            <Text style={styles.result}>当前选择: {selectedColor}</Text>
            <View style={[styles.colorPreview, { backgroundColor: colorMap[selectedColor] || '#000000' }]} />
          </View>
        </View>

        {/* 3. 城市选择器 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>3. 城市选择器</Text>
          <Text style={styles.description}>选择城市,展示单列选择器的实际应用</Text>
          <TouchableOpacity style={styles.button} onPress={showCityPicker}>
            <Text style={styles.buttonText}>打开选择器</Text>
          </TouchableOpacity>
          <Text style={styles.result}>当前选择: {selectedCity}</Text>
        </View>

        {/* 4. 日期选择器 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>4. 日期选择器</Text>
          <Text style={styles.description}>使用多列选择器实现日期选择功能</Text>
          <TouchableOpacity style={styles.button} onPress={showDatePicker}>
            <Text style={styles.buttonText}>打开选择器</Text>
          </TouchableOpacity>
          <Text style={styles.result}>当前选择: {selectedDate}</Text>
        </View>

        {/* 5. 商品规格选择器 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>5. 商品规格选择器</Text>
          <Text style={styles.description}>使用多列选择器实现商品规格选择</Text>
          <TouchableOpacity style={styles.button} onPress={showSpecPicker}>
            <Text style={styles.buttonText}>打开选择器</Text>
          </TouchableOpacity>
          <Text style={styles.result}>
            当前选择: {selectedSpec.color} / {selectedSpec.size} / 数量 {selectedSpec.quantity}
          </Text>
        </View>

        {/* 6. 省市区联动选择器 */}
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>6. 省市区联动选择器</Text>
          <Text style={styles.description}>展示联动选择器的使用方式</Text>
          <TouchableOpacity style={styles.button} onPress={showAreaPicker}>
            <Text style={styles.buttonText}>打开选择器</Text>
          </TouchableOpacity>
          <Text style={styles.result}>选择结果请查看控制台</Text>
        </View>
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  scrollView: {
    flex: 1,
  },
  scrollContent: {
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#333',
    textAlign: 'center',
    marginBottom: 30,
  },
  section: {
    backgroundColor: '#fff',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#333',
    marginBottom: 8,
  },
  description: {
    fontSize: 14,
    color: '#666',
    marginBottom: 12,
  },
  button: {
    backgroundColor: '#007AFF',
    paddingVertical: 12,
    paddingHorizontal: 24,
    borderRadius: 8,
    alignItems: 'center',
    marginBottom: 12,
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
  result: {
    fontSize: 14,
    color: '#333',
    fontWeight: '500',
  },
  colorPreviewContainer: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  colorPreview: {
    width: 40,
    height: 40,
    borderRadius: 8,
    borderWidth: 1,
    borderColor: '#ddd',
  },
});

export default PickerDemo;

在这里插入图片描述

📖 代码讲解

1. 导入库

import Picker from 'react-native-picker';

说明: 使用 react-native-picker 库时,导入方式不变,仍然是 import Picker from 'react-native-picker'

2. 单列选择器

使用场景: 选择编程语言、城市、颜色等单一维度的数据

关键配置:

  • pickerData: 数组形式的数据,如 ['Java', 'JavaScript', 'Python']
  • selectedValue: 默认选中的值,需要是数组形式,如 ['JavaScript']
  • onPickerConfirm: 确认选择时的回调,参数是选中的值数组

注意: 即使是单列选择器,selectedValue 和回调函数的参数也必须是数组形式,通过索引访问实际值:pickedValue[0]

3. 自定义样式配置

可配置的样式属性:

  • pickerToolBarBg: 工具栏背景色(RGBA 数组)
  • pickerBg: 选择器背景色(RGBA 数组)
  • pickerTitleColor: 标题文字颜色(RGBA 数组)
  • pickerConfirmBtnColor: 确认按钮文字颜色(RGBA 数组)
  • pickerCancelBtnColor: 取消按钮文字颜色(RGBA 数组)
  • pickerFontColor: 选项文字颜色(RGBA 数组)
  • pickerFontSize: 选项文字大小

颜色格式: 使用 RGBA 数组,如 [255, 255, 255, 1] 表示白色(R=255, G=255, B=255, A=1)

4. 多列选择器

使用场景: 日期选择、商品规格选择等需要多列的情况

关键配置:

  • pickerData: 数组的数组,如 [years, months, days]
  • selectedValue: 对应每列的默认值,如 ['2024年', '1月', '1日']
  • onPickerConfirm: 回调参数是多列选中的值数组

数据处理: 在回调函数中,需要对每列的数据进行处理,如将 2024年 转换为 2024

5. 联动选择器

使用场景: 省市区三级联动等需要根据前一列选择更新后一列的情况

实现方式:

  • 构建嵌套的数据结构
  • onPickerSelect 回调中动态更新后一列的数据(完整实现需要更多代码)
  • 示例中展示了基础的联动结构

6. 状态管理

推荐方式: 使用 useState 管理每个选择器的选中值

const [selectedLanguage, setSelectedLanguage] = useState('JavaScript');

注意事项:

  • 状态值应该存储实际使用的值(如 'JavaScript'),而不是显示值(如 '2024年'
  • onPickerConfirm 回调中更新状态
  • 使用状态值显示选择结果

7. 显示和隐藏选择器

显示选择器:

Picker.show();

隐藏选择器:

Picker.hide();

切换显示状态:

Picker.toggle();

检查选择器状态:

const isShown = Picker.isPickerShow();

最佳实践: 通常在按钮的 onPress 事件中调用 Picker.show(),在 onPickerConfirmonPickerCancel 回调中会自动隐藏选择器。

⚠️ 注意事项与最佳实践

1. Picker.init() 配置选项

常用配置:

  • pickerData: 选择器数据,数组或数组的数组
  • selectedValue: 默认选中的值(数组形式)
  • onPickerConfirm: 确认选择时的回调函数
  • onPickerCancel: 取消选择时的回调函数
  • pickerTitleText: 选择器标题

样式配置:

  • 颜色值使用 RGBA 数组格式:[R, G, B, A]
  • 每个颜色分量的范围是 0-255
  • 透明度 A 的范围是 0-1

2. 数据格式要求

单列选择器:

pickerData: ['选项1', '选项2', '选项3']
selectedValue: ['选项1']

多列选择器:

pickerData: [['a1', 'a2'], ['b1', 'b2']]
selectedValue: ['a1', 'b1']

联动选择器:

pickerData: {
  '选项1': ['子选项1-1', '子选项1-2'],
  '选项2': ['子选项2-1', '子选项2-2']
}

3. 回调函数参数

重要: onPickerConfirm 回调函数的参数类型需要特别处理

onPickerConfirm: (pickedValue) => {
  // pickedValue 可能是数组,也可能是单个值
  const values = Array.isArray(pickedValue) ? pickedValue : [pickedValue];
  console.log(values[0]); // 第一列的值
  console.log(values[1]); // 第二列的值(如果有)
}

最佳实践: 始终使用 Array.isArray() 检查参数类型,确保代码的健壮性。

4. 性能优化

  • 避免在渲染函数中重复调用 Picker.init()
  • 使用 useState 管理选中值,避免不必要的重新渲染
  • 对于大量数据,考虑分页或虚拟滚动

5. HarmonyOS 特殊处理

  • 确保原生代码正确链接
  • 在 HarmonyOS 设备上测试样式效果
  • 注意不同设备的屏幕适配

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

Logo

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

更多推荐