
UniAppx 实现安卓日历事件添加功能在移动应用开发中与系统日历集成是一项常见需求。本文将详细介绍如何在 UniAppx Vue3 uts uvue环境下通过 UTS 语言调用安卓原生 API实现日历事件的添加功能。功能概述实现的核心功能包括日历权限管理检查和请求安卓日历读写权限日历账户管理获取或创建日历账户 ID事件创建向系统日历中添加事件包含标题、描述、地点、时间等信息提醒设置为事件设置提前提醒操作日志记录所有操作步骤便于调试和用户反馈项目结构packageB/ └── addCalendar/ └── index.uvue # 主组件逻辑与样式模板结构设计模板结构简洁明了主要包含三个部分标题区域显示页面标题添加日历事件按钮区域包含一个添加日历事件按钮点击触发添加流程日志区域使用滚动视图显示操作日志用户可以查看详细的操作记录日期时间格式化组件包含两个核心的格式化函数formatDate将 Date 对象格式化为YYYY-MM-DD格式的字符串formatTime将 Date 对象格式化为HH:MM格式的字符串同时包含一个 padZero 辅助函数用于将单个数字补零。开始时间设为当前时间结束时间设为当前时间加一小时。时间戳转换getTimestamp 函数将日期字符串和时间字符串转换为毫秒级时间戳便于后续存储到日历数据库中。权限管理权限检查checkCalendarPermission 函数用于检查日历权限状态通过 UTSAndroid.getUniActivity() 获取当前 Activity然后调用 checkSystemPermissionGranted 检查 WRITE_CALENDAR 和 READ_CALENDAR 权限是否已授权。权限请求requestCalendarPermission 函数用于向用户请求日历权限使用 UTSAndroid.requestSystemPermission 发起权限请求包含成功和失败的回调处理。日历账户管理getCalendarId 函数负责获取或创建日历账户 ID首先查询本地日历账户account_typeLOCAL如果没有找到本地账户查找任何可见的日历账户visible1如果仍然没有找到返回默认账户 ID 1该函数使用安卓的 ContentResolver 和 ContentProvider 查询日历数据库。提醒设置addReminder 函数为创建的事件添加提醒创建 ContentValues 对象设置 event_id、minutes 和 method使用 ContentResolver 插入到 reminders 表中method1 表示使用通知提醒事件创建doAddCalendarEvent 函数是实际执行添加日历事件的核心逻辑获取 ContentResolver 对象获取日历账户 ID计算开始和结束时间的时间戳验证结束时间必须晚于开始时间创建 ContentValues 对象设置事件的各种属性日历ID、标题、描述、地点、开始时间、结束时间、时区、是否有闹钟使用 ContentResolver 插入事件到 events 表中如果创建成功获取事件 ID 并添加提醒使用 showToast 向用户反馈操作结果添加流程addCalendarEvent 函数是添加日历事件的入口函数记录操作日志检查日历权限如果没有权限先请求权限权限获取成功后执行添加如果有权限直接执行添加日志记录组件使用 ref 维护一个日志数组addLog 函数将日志消息添加到数组头部并在控制台打印。页面显示时会记录初始化日志。样式设计样式采用简洁的移动端设计风格页面背景色为浅灰色标题居中显示使用加粗字体按钮使用绿色背景白色文字日志区域使用白色背景圆角边框日志内容使用较小字体每条日志之间有分隔线安卓兼容性注意事项在 UniApp 安卓端开发时需要注意以下几点权限声明需要在 manifest.json 中声明日历相关权限API 兼容性不同安卓版本的日历 ContentProvider URI 可能不同类型转换UTS 语言中需要使用 .toInt()/.toLong() 进行类型转换异常处理操作日历可能抛出异常需要进行 try-catch 处理优化建议1. 权限设置引导如果用户拒绝权限后可以引导用户到系统设置页面开启权限。2. 事件重复功能可以增加事件重复设置支持每天、每周、每月等重复模式。3. 事件编辑和删除可以扩展功能支持对已添加的事件进行编辑和删除操作。4. 多账户支持可以让用户选择添加到哪个日历账户。总结通过以上实现我们完成了一个完整的安卓日历事件添加功能。核心要点包括使用 UTS 语言调用安卓原生 API正确处理权限请求和检查使用 ContentResolver 操作日历数据库完整的错误处理和日志记录良好的用户反馈机制这种实现方式具有良好的兼容性可以轻松应用于各种需要日历集成的场景。完整代码index.uvuetemplateviewclasscontainertextclasstitle添加日历事件/textviewclassbtn-groupbuttonclassbtn btn-successclickaddCalendarEvent添加日历事件/button/viewviewclasslog-areatextclasslog-title操作日志:/textscroll-viewclasslog-contentscroll-ytextv-for(log, index) in logs:keyindexclasslog-item{{ log }}/text/scroll-view/view/view/templatescriptsetuplangutsimport{ref}fromvue;consteventTitle测试日历事件;consteventDescription这是由uni-app-x自动创建的测试事件;consteventLocation北京市朝阳区;constnownewDate();constpadZero(num:number):string{returnnum10?0num:num;};constformatDate(date:Date):string{constyeardate.getFullYear();constmonthpadZero(date.getMonth()1);constdaypadZero(date.getDate());returnyear-month-day;};constformatTime(date:Date):string{consthourspadZero(date.getHours());constminutespadZero(date.getMinutes());returnhours:minutes;};conststartDateformatDate(now);conststartTimeformatTime(now);constendDateTimenewDate(now.getTime()60*60*1000);constendDateformatDate(endDateTime);constendTimeformatTime(endDateTime);constlogsrefstring[]([]);constaddLog(msg:string){consttimeformatTime(newDate());logs.value.unshift([time] msg);console.log(msg);};constgetTimestamp(dateStr:string,timeStr:string):number{constdatePartsdateStr.split(-);consttimePartstimeStr.split(:);constyearparseInt(dateParts[0]);constmonthparseInt(dateParts[1])-1;constdayparseInt(dateParts[2]);consthourparseInt(timeParts[0]);constminuteparseInt(timeParts[1]);returnnewDate(year,month,day,hour,minute).getTime();};constcheckCalendarPermission():boolean{constactivityUTSAndroid.getUniActivity();if(activitynull){addLog(获取Activity失败);returnfalse;}consthasPermissionUTSAndroid.checkSystemPermissionGranted(activity,[android.permission.WRITE_CALENDAR,android.permission.READ_CALENDAR]);addLog(日历权限状态: (hasPermission?已授权:未授权));returnhasPermission;};constrequestCalendarPermission(callback:()void){constactivityUTSAndroid.getUniActivity();if(activitynull){addLog(获取Activity失败);return;}constpermissions[android.permission.WRITE_CALENDAR,android.permission.READ_CALENDAR];UTSAndroid.requestSystemPermission(activity,permissions,(allRight:boolean,grantedPermissions:string[]){if(allRight){addLog(日历权限请求成功);callback();}else{addLog(用户拒绝了部分权限);uni.showToast({title:权限被拒绝,icon:none});}},(permissionDenied:boolean,deniedPermissions:string[]){addLog(用户拒绝了权限申请);uni.showToast({title:权限被拒绝,icon:none});});};constgetCalendarId():number{constactivityUTSAndroid.getUniActivity();if(activitynull){addLog(获取Activity失败);return-1;}try{constcontentResolveractivity.getContentResolver();if(contentResolvernull){addLog(获取ContentResolver失败);return-1;}constCALENDAR_URIandroid.net.Uri.parse(content://com.android.calendar/calendars);constprojectionarrayOf(_id,account_name,account_type,visible);constcursorcontentResolver.query(CALENDAR_URI,projection,account_type?,arrayOf(LOCAL),null);if(cursor!null){if(cursor.moveToFirst()){constidIndexcursor.getColumnIndex(_id);constcalendarIdcursor.getInt(idIndex);cursor.close();addLog(找到本地日历账户ID: calendarId);returncalendarId;}cursor.close();}constcursor2contentResolver.query(CALENDAR_URI,projection,visible?,arrayOf(1),null);if(cursor2!null){if(cursor2.moveToFirst()){constidIndexcursor2.getColumnIndex(_id);constcalendarIdcursor2.getInt(idIndex);cursor2.close();addLog(找到可见日历账户ID: calendarId);returncalendarId;}cursor2.close();}addLog(未找到日历账户使用默认ID: 1);return1;}catch(e){addLog(获取日历ID失败: e);return1;}};constaddReminder(contentResolver:android.content.ContentResolver,eventId:string|null,minutesBefore:number){if(eventIdnull)return;try{constreminderValuesnewandroid.content.ContentValues();reminderValues.put(event_id,eventId);reminderValues.put(minutes,minutesBefore.toInt());reminderValues.put(method,1);constREMINDERS_URIandroid.net.Uri.parse(content://com.android.calendar/reminders);contentResolver.insert(REMINDERS_URI,reminderValues);addLog(已设置提前minutesBefore分钟提醒);}catch(e){addLog(添加提醒失败: e);}};constdoAddCalendarEvent(){constactivityUTSAndroid.getUniActivity();if(activitynull){addLog(获取Activity失败);return;}try{constcontentResolveractivity.getContentResolver();if(contentResolvernull){addLog(获取ContentResolver失败);return;}constcalendarIdgetCalendarId();if(calendarId0){addLog(无法获取有效的日历ID);return;}conststartMillisgetTimestamp(startDate,startTime);constendMillisgetTimestamp(endDate,endTime);if(endMillisstartMillis){uni.showToast({title:结束时间必须晚于开始时间,icon:none});return;}constvaluesnewandroid.content.ContentValues();values.put(calendar_id,calendarId.toInt());values.put(title,eventTitle);values.put(description,eventDescription);values.put(eventLocation,eventLocation);values.put(dtstart,startMillis.toLong());values.put(dtend,endMillis.toLong());values.put(eventTimezone,java.util.TimeZone.getDefault().getID());values.put(hasAlarm,1);constEVENTS_URIandroid.net.Uri.parse(content://com.android.calendar/events);consteventUricontentResolver.insert(EVENTS_URI,values);if(eventUri!null){consteventIdeventUri.getLastPathSegment();addLog(事件创建成功ID: eventId);addReminder(contentResolver,eventId,10);uni.showToast({title:添加成功,icon:success});}else{addLog(事件创建失败);uni.showToast({title:添加失败,icon:none});}}catch(e){addLog(添加事件失败: e);uni.showToast({title:添加失败,icon:none});}};constaddCalendarEvent(){addLog(开始添加日历事件...);if(!checkCalendarPermission()){addLog(没有日历权限先请求权限);requestCalendarPermission((){doAddCalendarEvent();});return;}doAddCalendarEvent();};onShow((){addLog(页面加载点击按钮添加日历事件);});/scriptstyle.container{padding:20rpx;background-color:#f5f5f5;flex:1;}.title{font-size:36rpx;font-weight:bold;color:#333;text-align:center;margin-bottom:30rpx;}.btn-group{margin-top:30rpx;}.btn{margin-bottom:20rpx;font-size:32rpx;}.btn-success{background-color:#4cd964;color:#fff;}.log-area{margin-top:30rpx;background-color:#fff;padding:20rpx;border-radius:12rpx;}.log-title{font-size:28rpx;font-weight:bold;color:#333;margin-bottom:15rpx;}.log-content{max-height:300rpx;}.log-item{font-size:24rpx;color:#666;padding:8rpx 0;border-bottom:1rpx solid #f0f0f0;}/style