ReactNative项目OpenHarmony三方库集成实战指南:react-native-calendar-events(读取不到日历里新增的事件,待排查)

 更新时间:2026年04月09日 09:52:46   作者:早點睡390  
文章介绍了react-native-calendar-events库,这是一个强大的日历事件库,支持读写系统日历,实现日程管理功能,文章说明了其安装配置、核心功能、API使用方法,并提供了注意事项和完整示例,适用于React Native开发中的日历事件管理,感兴趣的朋友一起看看吧

项目基于 RN 0.72.90 开发

📋 前言

在移动应用开发中,日历事件管理是一项常见需求,特别是在日程管理、会议提醒、活动安排等场景中。react-native-calendar-events 是一个功能强大的日历事件库,提供了读取和写入系统日历的能力,支持创建、查询、更新和删除日历事件,是实现日程管理功能的理想选择。

🎯 库简介

基本信息

  • 库名称: react-native-calendar-events
  • 版本信息: 2.2.1 支持 RN 0.72 版本
  • 官方仓库: https://github.com/wmcmahan/react-native-calendar-events
  • 鸿蒙仓库: https://atomgit.com/openharmony-sig/rntpc_react-native-calendar-events
  • 主要功能:
    • 📅 日历权限管理
    • 📆 日历增删改查
    • 📝 事件创建与编辑
    • ⏰ 提醒设置
    • 🔁 重复规则配置
    • 👥 参与者管理
    • 📱 跨平台支持(iOS、Android、HarmonyOS)

为什么需要日历事件库?

特性原生方案react-native-calendar-events
跨平台一致性⚠️ 需分别开发✅ 统一 API
权限管理⚠️ 手动处理✅ 自动处理
事件操作⚠️ 需封装✅ 开箱即用
重复规则⚠️ 复杂实现✅ 内置支持
HarmonyOS 支持⚠️ 需适配✅ 完善适配

核心功能

功能说明HarmonyOS 支持
requestPermissions请求日历权限
checkPermissions检查权限状态
findCalendars获取所有日历
saveCalendar创建日历
removeCalendar删除日历
findEventById根据ID查询事件
fetchAllEvents获取所有事件
saveEvent创建/更新事件
removeEvent删除事件

兼容性验证

在以下环境验证通过:

  • RNOH: 0.72.90; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.2; ROM: 6.0.0

📦 安装步骤

1. 安装依赖

# RN 0.72 版本
npm install @react-native-ohos/react-native-calendar-events@2.2.1-rc.1
# 或者使用 yarn
yarn add @react-native-ohos/react-native-calendar-events@2.2.1-rc.1

2. 验证安装

安装完成后,检查 package.json 文件:

{
  "dependencies": {
    "@react-native-ohos/react-native-calendar-events": "^2.2.1-rc.1"
  }
}

🔧 HarmonyOS 平台配置 ⭐

1. 引入原生端代码

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

"dependencies": {
  "@react-native-ohos/react-native-calendar-events": "file:../../node_modules/@react-native-ohos/react-native-calendar-events/harmony/calendar_events.har"
}

点击右上角的 sync 按钮,或者在终端执行:

cd entry
ohpm install

2. 配置 CMakeLists

打开 entry/src/main/cpp/CMakeLists.txt,添加:

+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
# RNOH_BEGIN: manual_package_linking_1
+ add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-calendar-events/src/main/cpp" ./calendar_events)
# RNOH_END: manual_package_linking_1
# RNOH_BEGIN: manual_package_linking_2
+ target_link_libraries(rnoh_app PUBLIC rnoh_calendar_events)
# RNOH_END: manual_package_linking_2

3. 引入 CalendarEventPackage

打开 entry/src/main/cpp/PackageProvider.cpp,添加:

#include "RNOH/PackageProvider.h"
#include "generated/RNOHGeneratedPackage.h"
+ #include "CalendarEventsPackage.h"
using namespace rnoh;
std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
    return {
      std::make_shared<RNOHGeneratedPackage>(ctx),
      + std::make_shared<CalendarEventsPackage>(ctx)
    };
}

打开 entry/src/main/ets/RNPackagesFactory.ets,添加:

+ import { CalendarEventPackage } from '@react-native-ohos/react-native-calendar-events/ts';
export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new SamplePackage(ctx),
    + new CalendarEventPackage(ctx)
  ];
}

4. 配置权限

entry/src/main/module.json5 中添加日历权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.READ_CALENDAR",
        "reason": "$string:Access_calendar",
        "usedScene": {
          "abilities": [
            "FormAbility"
          ],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.WRITE_CALENDAR",
        "reason": "$string:Access_calendar",
        "usedScene": {
          "abilities": [
            "FormAbility"
          ],
          "when": "inuse"
        }
      }
    ]
  }
}

5. 添加权限说明

打开 entry/src/main/resources/base/element/string.json,添加权限说明:

{
  "string": [
    {
      "name": "Access_calendar",
      "value": "用于访问和管理日历事件"
    }
  ]
}

📖 API 详解

requestPermissions - 请求日历权限

请求日历访问权限,必须在访问日历数据之前调用。

类型(readOnly?: boolean) => Promise<'authorized' | 'denied' | 'restricted' | 'undetermined'>

参数

  • readOnly: 是否只请求读取权限,默认为 false

返回值

  • authorized: 已授权
  • denied: 已拒绝
  • restricted: 受限制(如家长控制)
  • undetermined: 未确定

使用场景

  • 应用首次启动时请求权限
  • 用户拒绝后重新请求
import RNCalendarEvents from 'react-native-calendar-events';
const requestPermission = async () => {
  const status = await RNCalendarEvents.requestPermissions();
  if (status === 'authorized') {
    console.log('日历权限已授权');
  } else {
    console.log('日历权限被拒绝');
  }
};

checkPermissions - 检查权限状态

检查当前的日历权限状态,不会弹出权限请求对话框。

类型(readOnly?: boolean) => Promise<'authorized' | 'denied' | 'restricted' | 'undetermined'>

参数

  • readOnly: 是否只检查读取权限,默认为 false

使用场景

  • 在执行日历操作前检查权限
  • 根据权限状态显示不同的 UI
import RNCalendarEvents from 'react-native-calendar-events';
const checkPermission = async () => {
  const status = await RNCalendarEvents.checkPermissions();
  console.log('当前权限状态:', status);
  return status === 'authorized';
};

findCalendars - 获取所有日历

获取设备上所有可用的日历列表。

类型() => Promise<Calendar[]>

返回值:日历对象数组,包含以下属性:

属性类型说明
idstring日历唯一标识
titlestring日历标题
sourceobject日历来源
colorstring日历颜色
allowsModificationsboolean是否允许修改
typestring日历类型

使用场景

  • 显示日历列表供用户选择
  • 获取默认日历 ID
import RNCalendarEvents from 'react-native-calendar-events';
const getCalendars = async () => {
  const calendars = await RNCalendarEvents.findCalendars();
  calendars.forEach((calendar) => {
    console.log('日历:', calendar.title, 'ID:', calendar.id);
  });
  return calendars;
};

saveCalendar - 创建日历

创建一个新的日历。

类型(options: CalendarOptions) => Promise<string>

参数

属性类型必填说明
titlestring日历标题
typestring日历类型
displayNamestring显示名称
colorstring日历颜色

返回值:新创建日历的 ID

import RNCalendarEvents from 'react-native-calendar-events';
const createCalendar = async () => {
  const calendarId = await RNCalendarEvents.saveCalendar({
    title: '我的日历',
    type: 'local',
    displayName: '个人日程',
    color: '#FF5722',
  });
  console.log('创建日历成功, ID:', calendarId);
  return calendarId;
};

removeCalendar - 删除日历

根据 ID 删除指定的日历。

类型(id: string) => Promise<boolean>

参数

  • id: 日历 ID

返回值:是否删除成功

import RNCalendarEvents from 'react-native-calendar-events';
const deleteCalendar = async (calendarId: string) => {
  const success = await RNCalendarEvents.removeCalendar(calendarId);
  console.log('删除日历:', success ? '成功' : '失败');
  return success;
};

findEventById - 根据 ID 查询事件

根据事件 ID 获取单个事件的详细信息。

类型(id: string) => Promise<Event | null>

参数

  • id: 事件 ID

返回值:事件对象,如果未找到则返回 null

事件对象属性

属性类型说明
idstring事件唯一标识
titlestring事件标题
locationstring事件地点
startDatestring开始时间
endDatestring结束时间
allDayboolean是否全天事件
descriptionstring事件描述
calendarIdstring所属日历 ID
attendeesarray参与者列表
recurrenceRuleobject重复规则
remindersarray提醒列表
import RNCalendarEvents from 'react-native-calendar-events';
const getEventById = async (eventId: string) => {
  const event = await RNCalendarEvents.findEventById(eventId);
  if (event) {
    console.log('事件标题:', event.title);
    console.log('开始时间:', event.startDate);
  }
  return event;
};

fetchAllEvents - 获取所有事件

获取指定时间范围内的所有事件。

类型(startDate: string, endDate: string, calendars?: string[]) => Promise<Event[]>

参数

参数类型必填说明
startDatestring开始时间(时间戳字符串)
endDatestring结束时间(时间戳字符串)
calendarsstring[]要查询的日历 ID 列表

使用场景

  • 显示日程列表
  • 日历视图数据源
  • 查询特定时间段的事件
import RNCalendarEvents from 'react-native-calendar-events';
const getEvents = async () => {
  const now = Date.now();
  const startDate = now.toString();
  const endDate = (now + 7 * 24 * 60 * 60 * 1000).toString();
  const events = await RNCalendarEvents.fetchAllEvents(startDate, endDate);
  events.forEach((event) => {
    console.log('事件:', event.title, '开始:', event.startDate);
  });
  return events;
};

saveEvent - 创建/更新事件

创建新事件或更新已有事件。如果 details 中包含 id,则更新该事件;否则创建新事件。

类型(title: string, details?: EventDetails, options?: SaveEventOptions) => Promise<string>

参数

title: 事件标题

details: 事件详情

属性类型必填说明
idstring事件 ID(更新时)
typenumber事件类型
titlestring事件标题
locationobject地点信息
startTimenumber开始时间(毫秒)
endTimenumber结束时间(毫秒)
isAllDayboolean是否全天事件
attendeearray参与者列表
timeZonestring时区
reminderTimenumber[]提醒时间列表
recurrenceRuleobject重复规则
descriptionstring事件描述

location 对象

属性类型说明
locationstring地点名称
longitudenumber经度
latitudenumber纬度

recurrenceRule 对象

属性类型说明
recurrenceFrequencynumber重复频率(1=每日,2=每周等)
expirenumber过期时间(0表示永不过期)

返回值:事件的 ID

import RNCalendarEvents from 'react-native-calendar-events';
const createEvent = async () => {
  const now = Date.now();
  const eventId = await RNCalendarEvents.saveEvent('项目会议', {
    title: '项目会议',
    startTime: now + 60 * 60 * 1000,
    endTime: now + 2 * 60 * 60 * 1000,
    location: {
      location: '会议室A',
      longitude: 116.397,
      latitude: 39.908,
    },
    description: '讨论项目进度',
    isAllDay: false,
    reminderTime: [now + 30 * 60 * 1000],
  });
  console.log('创建事件成功, ID:', eventId);
  return eventId;
};
const updateEvent = async (eventId: string) => {
  const now = Date.now();
  const newEventId = await RNCalendarEvents.saveEvent('项目会议(已更新)', {
    id: eventId,
    title: '项目会议(已更新)',
    startTime: now + 2 * 60 * 60 * 1000,
    endTime: now + 3 * 60 * 60 * 1000,
    description: '会议时间已调整',
  });
  console.log('更新事件成功');
  return newEventId;
};

removeEvent - 删除事件

根据 ID 删除指定的事件。

类型(id: string, options?: { futureEvents?: boolean }) => Promise<boolean>

参数

参数类型必填说明
idstring事件 ID
futureEventsboolean是否删除未来的重复事件

返回值:是否删除成功

import RNCalendarEvents from 'react-native-calendar-events';
const deleteEvent = async (eventId: string) => {
  const success = await RNCalendarEvents.removeEvent(eventId);
  console.log('删除事件:', success ? '成功' : '失败');
  return success;
};

📋 完整示例

import React, { useState, useEffect } from 'react';
import {
  View,
  Text,
  StyleSheet,
  ScrollView,
  TouchableOpacity,
  SafeAreaView,
  StatusBar,
  Alert,
  TextInput,
  Switch,
  TouchableWithoutFeedback,
} from 'react-native';
import RNCalendarEvents from 'react-native-calendar-events';
interface Calendar {
  id: string;
  title: string;
  type?: string;
  displayName?: string;
}
interface Event {
  id: string;
  title: string;
  startDate: string;
  endDate?: string;
  location?: string;
  description?: string;
  allDay?: boolean;
}
const App: React.FC = () => {
  const [calendars, setCalendars] = useState<Calendar[]>([]);
  const [events, setEvents] = useState<Event[]>([]);
  const [isAuthorized, setIsAuthorized] = useState(false);
  const [showForm, setShowForm] = useState(false);
  const [eventTitle, setEventTitle] = useState('');
  const [eventDescription, setEventDescription] = useState('');
  const [eventLocation, setEventLocation] = useState('');
  const [isAllDay, setIsAllDay] = useState(false);
  useEffect(() => {
    checkPermission();
  }, []);
  const checkPermission = async () => {
    const status = await RNCalendarEvents.checkPermissions();
    setIsAuthorized(status === 'authorized');
    if (status === 'authorized') {
      loadCalendars();
      loadEvents();
    }
  };
  const requestPermission = async () => {
    const status = await RNCalendarEvents.requestPermissions();
    setIsAuthorized(status === 'authorized');
    if (status === 'authorized') {
      loadCalendars();
      loadEvents();
    } else {
      Alert.alert('权限被拒绝', '请在设置中允许访问日历');
    }
  };
  const loadCalendars = async () => {
    try {
      const calendarList = await RNCalendarEvents.findCalendars();
      setCalendars(calendarList);
    } catch (error) {
      console.error('获取日历失败:', error);
    }
  };
  const loadEvents = async () => {
    try {
      const now = new Date();
      const startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString();
      const endDate = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString();
      const eventList = await RNCalendarEvents.fetchAllEvents(startDate, endDate);
      setEvents(eventList);
    } catch (error) {
      console.error('获取事件失败:', error);
    }
  };
  const createEvent = async () => {
    if (!eventTitle.trim()) {
      Alert.alert('提示', '请输入事件标题');
      return;
    }
    try {
      const now = new Date();
      let startDate: string;
      let endDate: string;
      if (isAllDay) {
        const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
        const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
        startDate = today.toISOString();
        endDate = tomorrow.toISOString();
      } else {
        startDate = new Date(now.getTime() + 60 * 60 * 1000).toISOString();
        endDate = new Date(now.getTime() + 2 * 60 * 60 * 1000).toISOString();
      }
      const eventId = await RNCalendarEvents.saveEvent(eventTitle, {
        startDate,
        endDate,
        location: eventLocation || undefined,
        description: eventDescription,
        allDay: isAllDay,
      });
      Alert.alert('成功', '事件创建成功');
      setShowForm(false);
      setEventTitle('');
      setEventDescription('');
      setEventLocation('');
      setIsAllDay(false);
      loadEvents();
    } catch (error) {
      console.error('创建事件失败:', error);
      Alert.alert('错误', '创建事件失败');
    }
  };
  const deleteEvent = async (eventId: string) => {
    Alert.alert('确认删除', '确定要删除这个事件吗?', [
      { text: '取消', style: 'cancel' },
      {
        text: '删除',
        style: 'destructive',
        onPress: async () => {
          try {
            await RNCalendarEvents.removeEvent(eventId);
            Alert.alert('成功', '事件已删除');
            loadEvents();
          } catch (error) {
            console.error('删除事件失败:', error);
            Alert.alert('错误', '删除事件失败');
          }
        },
      },
    ]);
  };
  const formatDate = (dateString: string) => {
    try {
      const date = new Date(dateString);
      if (isNaN(date.getTime())) {
        return dateString;
      }
      const year = date.getUTCFullYear();
      const month = String(date.getUTCMonth() + 1).padStart(2, '0');
      const day = String(date.getUTCDate()).padStart(2, '0');
      const hours = String(date.getUTCHours()).padStart(2, '0');
      const minutes = String(date.getUTCMinutes()).padStart(2, '0');
      return `${year}-${month}-${day} ${hours}:${minutes}`;
    } catch {
      return dateString;
    }
  };
  const getLocationText = (location: Event['location']) => {
    return location || '';
  };
  if (!isAuthorized) {
    return (
      <SafeAreaView style={styles.container}>
        <StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
        <View style={styles.permissionContainer}>
          <Text style={styles.permissionTitle}>日历权限</Text>
          <Text style={styles.permissionText}>
            需要日历权限才能管理您的日程事件
          </Text>
          <TouchableOpacity style={styles.permissionButton} onPress={requestPermission}>
            <Text style={styles.permissionButtonText}>授权访问日历</Text>
          </TouchableOpacity>
        </View>
      </SafeAreaView>
    );
  }
  return (
    <SafeAreaView style={styles.container}>
      <StatusBar barStyle="dark-content" backgroundColor="#FFFFFF" />
      <View style={styles.header}>
        <Text style={styles.headerTitle}>日历事件管理</Text>
        <TouchableOpacity
          style={styles.addButton}
          onPress={() => setShowForm(true)}
        >
          <Text style={styles.addButtonText}>+ 新建</Text>
        </TouchableOpacity>
      </View>
      <ScrollView style={styles.content}>
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>日历列表 ({calendars.length})</Text>
          {calendars.length === 0 ? (
            <Text style={styles.emptyText}>暂无日历</Text>
          ) : (
            calendars.map((calendar, index) => (
              <View key={calendar.id || index} style={styles.calendarItem}>
                <View style={styles.calendarColor} />
                <View style={styles.calendarInfo}>
                  <Text style={styles.calendarTitle}>{calendar.title || '未命名日历'}</Text>
                  <Text style={styles.calendarType}>类型: {calendar.type || '本地'}</Text>
                </View>
              </View>
            ))
          )}
        </View>
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>近期事件 ({events.length})</Text>
          {events.length === 0 ? (
            <View style={styles.emptyContainer}>
              <Text style={styles.emptyText}>暂无事件</Text>
              <Text style={styles.emptyHint}>点击右上角"新建"创建事件</Text>
            </View>
          ) : (
            events.map((event) => (
              <View key={event.id} style={styles.eventItem}>
                <View style={styles.eventHeader}>
                  <Text style={styles.eventTitle}>{event.title}</Text>
                  <TouchableOpacity onPress={() => deleteEvent(event.id)}>
                    <Text style={styles.deleteText}>删除</Text>
                  </TouchableOpacity>
                </View>
                <Text style={styles.eventTime}>开始: {formatDate(event.startDate)}</Text>
                {event.endDate && (
                  <Text style={styles.eventTime}>结束: {formatDate(event.endDate)}</Text>
                )}
                {event.location && (
                  <Text style={styles.eventLocation}>📍 {getLocationText(event.location)}</Text>
                )}
                {event.description && (
                  <Text style={styles.eventDescription}>{event.description}</Text>
                )}
              </View>
            ))
          )}
        </View>
      </ScrollView>
      {showForm && (
        <TouchableWithoutFeedback onPress={() => setShowForm(false)}>
          <View style={styles.overlay}>
            <TouchableWithoutFeedback onPress={() => {}}>
              <View style={styles.formContainer}>
                <Text style={styles.formTitle}>新建事件</Text>
                <Text style={styles.inputLabel}>事件标题</Text>
                <TextInput
                  style={styles.input}
                  placeholder="请输入事件标题"
                  value={eventTitle}
                  onChangeText={setEventTitle}
                />
                <Text style={styles.inputLabel}>地点(可选)</Text>
                <TextInput
                  style={styles.input}
                  placeholder="请输入地点"
                  value={eventLocation}
                  onChangeText={setEventLocation}
                />
                <Text style={styles.inputLabel}>描述(可选)</Text>
                <TextInput
                  style={[styles.input, styles.textArea]}
                  placeholder="请输入描述"
                  value={eventDescription}
                  onChangeText={setEventDescription}
                  multiline
                />
                <View style={styles.switchRow}>
                  <Text style={styles.switchLabel}>全天事件</Text>
                  <Switch value={isAllDay} onValueChange={setIsAllDay} />
                </View>
                <View style={styles.formButtons}>
                  <TouchableOpacity
                    style={[styles.formButton, styles.cancelButton]}
                    onPress={() => setShowForm(false)}
                  >
                    <Text style={styles.cancelButtonText}>取消</Text>
                  </TouchableOpacity>
                  <TouchableOpacity
                    style={[styles.formButton, styles.confirmButton]}
                    onPress={createEvent}
                  >
                    <Text style={styles.confirmButtonText}>创建</Text>
                  </TouchableOpacity>
                </View>
              </View>
            </TouchableWithoutFeedback>
          </View>
        </TouchableWithoutFeedback>
      )}
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5F5F5',
  },
  permissionContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  permissionTitle: {
    fontSize: 24,
    fontWeight: '700',
    color: '#333333',
    marginBottom: 12,
  },
  permissionText: {
    fontSize: 16,
    color: '#666666',
    textAlign: 'center',
    marginBottom: 24,
  },
  permissionButton: {
    backgroundColor: '#007AFF',
    paddingHorizontal: 32,
    paddingVertical: 14,
    borderRadius: 8,
  },
  permissionButtonText: {
    color: '#FFFFFF',
    fontSize: 16,
    fontWeight: '600',
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: 16,
    backgroundColor: '#FFFFFF',
    borderBottomWidth: 1,
    borderBottomColor: '#E5E5EA',
  },
  headerTitle: {
    fontSize: 20,
    fontWeight: '700',
    color: '#333333',
  },
  addButton: {
    backgroundColor: '#007AFF',
    paddingHorizontal: 16,
    paddingVertical: 8,
    borderRadius: 6,
  },
  addButtonText: {
    color: '#FFFFFF',
    fontSize: 14,
    fontWeight: '600',
  },
  content: {
    flex: 1,
    padding: 16,
  },
  section: {
    backgroundColor: '#FFFFFF',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#333333',
    marginBottom: 12,
  },
  calendarItem: {
    flexDirection: 'row',
    alignItems: 'center',
    paddingVertical: 12,
    borderBottomWidth: 1,
    borderBottomColor: '#F0F0F0',
  },
  calendarColor: {
    width: 12,
    height: 12,
    borderRadius: 6,
    backgroundColor: '#007AFF',
    marginRight: 12,
  },
  calendarInfo: {
    flex: 1,
  },
  calendarTitle: {
    fontSize: 16,
    color: '#333333',
    fontWeight: '500',
  },
  calendarType: {
    fontSize: 14,
    color: '#999999',
    marginTop: 2,
  },
  emptyContainer: {
    alignItems: 'center',
    paddingVertical: 32,
  },
  emptyText: {
    fontSize: 16,
    color: '#999999',
  },
  emptyHint: {
    fontSize: 14,
    color: '#CCCCCC',
    marginTop: 8,
  },
  eventItem: {
    paddingVertical: 12,
    borderBottomWidth: 1,
    borderBottomColor: '#F0F0F0',
  },
  eventHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  eventTitle: {
    fontSize: 16,
    fontWeight: '600',
    color: '#333333',
    flex: 1,
  },
  deleteText: {
    fontSize: 14,
    color: '#FF3B30',
    marginLeft: 12,
  },
  eventTime: {
    fontSize: 14,
    color: '#666666',
    marginTop: 4,
  },
  eventLocation: {
    fontSize: 14,
    color: '#666666',
    marginTop: 4,
  },
  eventDescription: {
    fontSize: 14,
    color: '#999999',
    marginTop: 4,
  },
  overlay: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(0,0,0,0.5)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  formContainer: {
    backgroundColor: '#FFFFFF',
    borderRadius: 12,
    padding: 20,
    width: '90%',
    maxWidth: 400,
  },
  formTitle: {
    fontSize: 20,
    fontWeight: '700',
    color: '#333333',
    marginBottom: 20,
    textAlign: 'center',
  },
  inputLabel: {
    fontSize: 14,
    color: '#666666',
    marginBottom: 4,
  },
  input: {
    borderWidth: 1,
    borderColor: '#E5E5EA',
    borderRadius: 8,
    paddingHorizontal: 16,
    paddingVertical: 12,
    fontSize: 16,
    marginBottom: 12,
  },
  textArea: {
    height: 80,
    textAlignVertical: 'top',
  },
  switchRow: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    paddingVertical: 8,
    marginBottom: 20,
  },
  switchLabel: {
    fontSize: 16,
    color: '#333333',
  },
  formButtons: {
    flexDirection: 'row',
    gap: 12,
  },
  formButton: {
    flex: 1,
    paddingVertical: 14,
    borderRadius: 8,
    alignItems: 'center',
  },
  cancelButton: {
    backgroundColor: '#F0F0F0',
  },
  cancelButtonText: {
    fontSize: 16,
    color: '#666666',
    fontWeight: '600',
  },
  confirmButton: {
    backgroundColor: '#007AFF',
  },
  confirmButtonText: {
    fontSize: 16,
    color: '#FFFFFF',
    fontWeight: '600',
  },
});
export default App;

⚠️ 注意事项

1. 权限配置

  • 必须在 module.json5 中配置 READ_CALENDARWRITE_CALENDAR 权限
  • 权限说明文案需要在 string.json 中定义
  • 首次使用时需要调用 requestPermissions() 请求权限

2. 时间格式

  • fetchAllEvents 的参数需要使用时间戳字符串
  • saveEventstartTimeendTime 使用毫秒时间戳
  • 时间戳需要转换为字符串传递

3. 日历 ID

  • 创建事件前需要确保有可用的日历
  • 可以通过 findCalendars() 获取日历列表
  • 建议使用系统默认日历或创建专用日历

4. 事件更新

  • 更新事件时需要在 details 中传入事件 ID
  • 更新会覆盖原有的事件信息
  • 建议先查询事件详情再进行更新

5. 重复事件

  • recurrenceFrequency 值:1=每日,2=每周,3=每月,4=每年
  • expire 为 0 表示永不过期
  • 删除重复事件时可以指定 futureEvents 参数

6. 错误处理

  • 所有 API 都返回 Promise,需要使用 try-catch 捕获错误
  • 权限被拒绝时需要引导用户到设置中开启
  • 日历操作失败时需要给用户明确的错误提示

7. 性能优化

  • 查询事件时指定合理的时间范围
  • 避免频繁查询大量事件
  • 建议缓存日历列表减少查询次数

到此这篇关于ReactNative项目OpenHarmony三方库集成实战指南:react-native-calendar-events(读取不到日历里新增的事件,待排查)的文章就介绍到这了,更多相关react-native-calendar-events库内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React教程之封装一个Portal可复用组件的方法

    React教程之封装一个Portal可复用组件的方法

    react的核心之一是组件,下面这篇文章主要给大家介绍了关于React教程之封装一个Portal可复用组件的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2018-01-01
  • React 组件通信的多种方式与最佳实践

    React 组件通信的多种方式与最佳实践

    在现代前端开发中,组件化是 React 的核心理念之一,组件之间的通信是构建复杂应用的重要组成部分,在这篇文章中,我们将深入探讨 React 组件通信的多种方式,包括它们的优缺点、使用场景以及最佳实践,需要的朋友可以参考下
    2024-11-11
  • Router添加路由拦截方法讲解

    Router添加路由拦截方法讲解

    在vue项目中使用vue-router做页面跳转时,路由的方式有两种,一种是静态路由,另一种是动态路由。而要实现对路由的控制需要使用vuex和router全局守卫进行判断拦截
    2023-03-03
  • React实现音频文件上传与试听

    React实现音频文件上传与试听

    本文主要介绍了React实现音频文件上传与试听,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-09-09
  • Next.js搭建Monorepo组件库文档实现详解

    Next.js搭建Monorepo组件库文档实现详解

    这篇文章主要为大家介绍了Next.js搭建Monorepo组件库文档,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 面试官常问React的生命周期问题

    面试官常问React的生命周期问题

    在react面试中,面试官经常会问我们一些关于react的生命周期问题,今天特此分享本文给大家详细介绍下,感兴趣的朋友跟随小编一起看看吧
    2021-08-08
  • React启动时webpack版本冲突报错的解决办法

    React启动时webpack版本冲突报错的解决办法

    在启动React应用时,遇到Webpack版本不匹配导致的运行错误,解决方法包括删除全局及局部的webpack和webpack-cli,然后根据项目需求安装特定版本的webpack,本文通过代码示例给大家介绍的非常详细,需要的朋友可以参考下
    2024-09-09
  • react-native android状态栏的实现

    react-native android状态栏的实现

    这篇文章主要介绍了react-native android状态栏的实现,使状态栏颜色与App颜色一致,使用户界面更加整体。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • react事件回调中使用防抖失败的常见错误和正确使用方式

    react事件回调中使用防抖失败的常见错误和正确使用方式

    这篇文章主要介绍了react事件回调中使用防抖失败的常见错误和正确使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • React使用PropTypes实现类型检查功能

    React使用PropTypes实现类型检查功能

    这篇文章主要介绍了React高级指引中使用PropTypes实现类型检查功能的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-02-02

最新评论