安卓往系統中添加日程提醒,吭比較多。 首先有個需求(仿製 ios 日曆),為什麼仿製ios呢?這個得問產品了,我只是一個搬磚的程式員 (*´艸`) 捂嘴 大致有日期,時間,重覆設置,自定義重覆設置,位置提醒設置 跟系統日曆的設置類似,畢竟需要同步到系統,所以設置上面保持規範,都是設置好日期時間,然後 ...
安卓往系統中添加日程提醒,吭比較多。
首先有個需求(仿製 ios 日曆),為什麼仿製ios呢?這個得問產品了,我只是一個搬磚的程式員 (*´艸`) 捂嘴
大致有日期,時間,重覆設置,自定義重覆設置,位置提醒設置
跟系統日曆的設置類似,畢竟需要同步到系統,所以設置上面保持規範,都是設置好日期時間,然後重覆項。
一般的日曆添加也比較簡單(重覆規則比較煩),先看效果圖
添加日曆首先得有一個賬戶,這個自己定義一個就行了
/** * 添加日曆賬戶,賬戶創建成功則返回賬戶id,否則返回-1 */ private fun addCalendarAccount(context: Context): Long { val timeZone: TimeZone = TimeZone.getDefault() val value = ContentValues() value.put(CalendarContract.Calendars.NAME, CALENDARS_NAME) value.put(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME) value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE) value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, CALENDARS_DISPLAY_NAME) value.put(CalendarContract.Calendars.VISIBLE, 1)//設置日曆可見 value.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.BLUE) //使用的許可權等級 value.put( CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER ) value.put(CalendarContract.Calendars.SYNC_EVENTS, 1)//同步到系統 value.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timeZone.getID()) value.put(CalendarContract.Calendars.OWNER_ACCOUNT, CALENDARS_ACCOUNT_NAME) value.put(CalendarContract.Calendars.CAN_ORGANIZER_RESPOND, 0) var calendarUri = Uri.parse(CALENDER_URL) calendarUri = calendarUri.buildUpon() .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true") .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME) .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE) .build() val result = context.contentResolver.insert(calendarUri, value) log("addCalendarAccount result $result") return if (result == null) -1 else ContentUris.parseId(result) }
後面開始常規的日曆添加操作,在UI上選擇好時間,調用系統方法,同步到系統日曆
/** * 添加日曆事件 */ private fun addCalendarEvent( context: Context?, reminderTitle: String?, description: String?, reminderTime: Long, rule: String? = null, ): Boolean { log("addCalendarEvent $rule") if (context == null) { return false } val calId = checkAndAddCalendarAccount(context) //獲取日曆賬戶的id log("addCalendarEvent calId $calId") if (calId < 0) { //獲取賬戶id失敗直接返回,添加日曆事件失敗 return false } deleteCalendarEvent(context, reminderTitle, description) //添加日曆事件 val mCalendar = Calendar.getInstance() mCalendar.timeInMillis = reminderTime //設置開始時間 val start = mCalendar.time.time val event = ContentValues() event.put(CalendarContract.Events.TITLE, reminderTitle) event.put(CalendarContract.Events.DESCRIPTION, description) event.put(CalendarContract.Events.CALENDAR_ID, calId) //插入賬戶的id event.put(CalendarContract.Events.DTSTART, start)//開始時間 //結束時間 ,如果事件是每天/周,那麼就沒有結束時間 event.put(CalendarContract.Events.DTEND, start) event.put(CalendarContract.Events.HAS_ALARM, 1) //設置有鬧鐘提醒 event.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().id) //時區 if (rule != null) { event.put(CalendarContract.Events.RRULE, rule) } val calendarEvent = context.contentResolver.insert(Uri.parse(CALENDER_EVENT_URL), event) ?: return false log("addCalendarEvent newEvent $calendarEvent") //事件提醒的設定 val reminders = ContentValues() reminders.put(CalendarContract.Reminders.EVENT_ID, ContentUris.parseId(calendarEvent)) reminders.put(CalendarContract.Reminders.MINUTES, 0) // 提前幾分鐘提醒 reminders.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT)//提醒次數 val remindUri = context.contentResolver.insert(Uri.parse(CALENDER_REMINDER_URL), reminders) log("addCalendarEvent uri $remindUri") return remindUri != null }
很多設置其實都是固定值,或者系統規定配置,只需要傳入一個日期時間
這樣基本的添加其實已經完成,但是如果需要重覆,自定義等操作,就複雜許多
重覆提醒
首先重覆提醒,也就是上圖中的常規選擇,也就是每小時,每天,每周每年等
在事件中添加重覆規則
val rule = StringBuilder() when (repeatType) { //永不 RemindRepeatType.NEVER.value -> { return null } //每小時 RemindRepeatType.EVERY_HOUR.value -> { rule.append("FREQ=HOURLY;INTERVAL=1") } //每天 RemindRepeatType.EVERY_DAY.value -> { rule.append("FREQ=DAILY;INTERVAL=1") } //工作日 RemindRepeatType.EVERY_WORK_DAY.value -> { rule.append("FREQ=WEEKLY;INTERVAL=1") for (i in 0 until 5) { if (i <= byDayArray.size) { if (i == 0) { rule.append(";BYDAY=${byDayArray[i]}") } else { rule.append(",${byDayArray[i]}") } } } } //周末 RemindRepeatType.EVERY_WEEKEND.value -> { rule.append("FREQ=WEEKLY;INTERVAL=1;BYDAY=${byDayArray[5]},${byDayArray[6]}") } //每周 RemindRepeatType.EVERY_WEEK.value -> { rule.append("FREQ=WEEKLY;INTERVAL=1") } //每兩周 RemindRepeatType.EVERY_TWO_WEEKS.value -> { rule.append("FREQ=WEEKLY;INTERVAL=2") } //每月 RemindRepeatType.EVERY_MONTH.value -> { rule.append("FREQ=MONTHLY;INTERVAL=1") } //每3個月 RemindRepeatType.EVERY_THREE_MONTHS.value -> { rule.append("FREQ=MONTHLY;INTERVAL=3") } //每6個月 RemindRepeatType.EVERY_SIX_MONTHS.value -> { rule.append("FREQ=MONTHLY;INTERVAL=6") } //每年 RemindRepeatType.EVERY_YEAR.value -> { rule.append("FREQ=YEARLY;INTERVAL=1") } }
規則都是通過字元自定義拼接,可讀性比較差
FREQ :表示重覆規則的類型, 必須定義
HOURLY:小時、DAILY:天、WEEKLY:周、MONTHLY:月、YEARLY:年
INTERVAL :重覆間隔數
必須為正整數,預設值為1,表示每小時、每天
BYDAY : MO(周一),TU(周二),WE(周三),TU(周四),FR(周五),SA(周六),SU(周日)
比如每個周末重覆:
FREQ=WEEKLY;INTERVAL=1;BYDAY=SA,SU
最後需要添加 ;WKST=SU ,表示從周幾開始,硬性規定。
這隻是常規的重覆項,如果需要自定義重覆項,也差距不大。
//1:日 2:周 3:月 4:年 when (customRepeatFreq) { 1 -> rule.append("FREQ=DAILY;INTERVAL=$customRepeatInterval") 2 -> { rule.append("FREQ=WEEKLY;INTERVAL=$customRepeatInterval") customRepeatItems?.let { for (i in it.indices) { val index = it[i] - 1 if (index <= byDayArray.size) { if (i == 0) { rule.append(";BYDAY=${byDayArray[index]}") } else { rule.append(",${byDayArray[index]}") } } } } } 3 -> { rule.append("FREQ=MONTHLY;INTERVAL=$customRepeatInterval") customRepeatItems?.let { for (i in it.indices) { val index = it[i] - 1 if (index <= byMonthDayArray.size) { if (i == 0) { rule.append(";BYMONTHDAY=${byMonthDayArray[index]}") } else { rule.append(",${byMonthDayArray[index]}") } } } } } 4 -> { rule.append("FREQ=YEARLY;INTERVAL=$customRepeatInterval") customRepeatItems?.let { for (i in it.indices) { val index = it[i] - 1 if (index <= byMonthArray.size) { if (i == 0) { rule.append(";BYMONTH=${byMonthArray[index]}") } else { rule.append(",${byMonthArray[index]}") } } } } } }
自定義重覆有重覆評率,跟日期類型,自定義時間,比如每個月的1號重覆
上面邏輯是判斷自定義類型 customRepeatFreq,選擇的是日,周,月,年,然後是自定義重覆數 customRepeatInterval,最後是自定義日期 customRepeatItems,具體選擇的某天或者某幾天。
所以把系統規定好的標識都定義成集合,方便動態添加
// BYDAY 周 private val byDayArray = arrayOf("MO", "TU", "WE", "TH", "FR", "SA", "SU") // BYMONTHDAY 月-天 private val byMonthDayArray = arrayOf( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 ) // BYMONTH 年-月 private val byMonthArray = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)View Code
重點就是在於規範的拼接,需要跟業務結合,然後同步到系統
然後是修改日曆,這邊修改也比較繁瑣,並且重覆日曆修改沒有系統那種關聯性,系統可以識別一樣的標題同步修改,程式只能自己迴圈修改,並且每次修改都要獲取許可權,相容性結合業務不好操作,所以綜合考慮,採取先刪除在添加。
而且居然需要修改,肯定對之前的提醒不滿意,在次添加新的提醒也符合用戶習慣。
日曆操作對時間格式要求高,需要規定好時間格式,加強判斷。