Implementation of HarmonyOS Schedule Feature
SoulHarbor

SoulHarbor @isoneheart

Joined:
Jun 12, 2025

Implementation of HarmonyOS Schedule Feature

Publish Date: Jun 14
0 0

I. Introduction and Preparation

Overall Introduction

This project focuses on implementing the schedule feature in HarmonyOS, utilizing environments such as HarmonyOS 5.0.0 and above, along with DevEco Studio 5.0.0 and above.

An overview of the application's overall layout design is provided, including the configuration of the schedule module (ScheduleView.ets) and the key code implementation for the schedule entry UI in the pages>MainEntry.ets file under the products layer. This demonstrates how to integrate the calendar module component, construct a navigation container, and implement tab switching for the bottom navigation bar.

The schedule functionality module and its implementation process are elaborated in detail. The UI structure of the schedule module consists of core components such as the main schedule view (supporting switching between monthly and weekly views, with highlighting of today's schedule), the schedule details page (displaying time, location, participants, and reminder settings), and the add/edit page (providing form input, map location selection, and attachment upload functions). Special emphasis is placed on the creation and implementation code of the ScheduleView.ets file, including loading schedule data, implementing search functionality, and adding new schedules.

The development of calendar components is also introduced, including the code implementation of the calendar header (ScheduleTitle.ets, which implements functions like year-month switching and lunar calendar toggle) and the calendar date view (CalenderView.ets, which constructs date data display for monthly or weekly views and enables date selection).
The code implementation of the calendar schedule item (ScheduleItem.ets) is described, covering functions such as displaying schedule information, formatting time, determining if a schedule has ended, and clicking to navigate to the schedule details page.

Basic Preparation

(1) Device Type: Huawei smartphone (bar phone)

(2) HarmonyOS Version: HarmonyOS 5.0.0 Release and above

(3) DevEco Studio Version: DevEco Studio 5.0.0 Release and above

(4) HarmonyOS SDK Version: HarmonyOS 5.0.0 Release SDK and above

II. Application Overall Construction and Layout Design

Module Design

Schedule Module: ScheduleView.ets

III. Implementation of Schedule Entry UI

Key code implementation in the pages>MainEntry.ets file under the products layer:

// Import modules: bottom navigation configuration, functional module views, type definitions, etc.
import { TAB_LIST } from '../constants/Constants';
import { AgencyTaskView } from '@ohos_agcit/office_attendance_agency';
import { ScheduleView } from '@ohos_agcit/office_attendance_schedule';
import { TabListItem } from '@ohos_agcit/office_attendance_agency';
import { CheckInView } from '@ohos_agcit/office_attendance_checkin';
import { CommonConstants, MainEntryVM } from '@ohos_agcit/office_attendance_common_lib';
import { MineView } from '@ohos_agcit/office_attendance_account';
import { router } from '@kit.ArkUI';

// --------------------------
// Main entry page builder (for external calls)
// --------------------------
@Builder
export function mainEntryBuilder() {
  MainEntry();
}

@Entry // Mark as the application entry component
@ComponentV2 // Declare as an ArkUI V2 component
struct MainEntry {
  // Router management singleton
  vm: MainEntryVM = MainEntryVM.instance;

  // Page state control
  @Provider() isPageShow: boolean = false; // Whether the page is displayed
  @Provider() currentTabIndex: number = this.vm.curIndex; // Current selected tab index
  onPageShow(): void {
    this.isPageShow = true; // Update state when the page is displayed
  }

  onPageHide(): void {
    this.isPageShow = false; // Update state when the page is hidden
  }

  build() {
    Navigation(this.vm.navStack) { // Navigation container (manages page stack)
      // Bottom navigation bar configuration
      Tabs({ 
        barPosition: BarPosition.End, // Navigation bar position (bottom)
        index: this.vm.curIndex // Current selected index
      }) {
        // Tab1: Attendance punch-in module
        TabContent() {
          CheckInView(); // Load the punch-in component
        }
        .tabBar(this.tabBarBuilder(TAB_LIST[0], 0)); // Bind navigation bar style

        // Tab2: Pending tasks module
        TabContent() {
          AgencyTaskView(); // Load the pending tasks component
        }
        .tabBar(this.tabBarBuilder(TAB_LIST[1], 1));

        // Tab3: Schedule management module
        TabContent() {
          ScheduleView(); // Load the schedule component
        }
        .tabBar(this.tabBarBuilder(TAB_LIST[2], 2));

        // Tab4: Personal center module
        TabContent() {
          // MineView({ // Load the personal center component
          //   callback: () => { // Logout callback
          //     router.replaceUrl({ url: 'pages/Login' }) // Navigate to the login page
          //   }
          // });
        }
        .tabBar(this.tabBarBuilder(TAB_LIST[3], 3));
      }
      // Navigation bar style configuration
      .scrollable(false) // Disable swipe switching
      .barHeight(80) // Navigation bar height
      .height(CommonConstants.FULL_HEIGHT) // Full screen height
      .animationDuration(0) // Disable switching animation
      .barMode(BarMode.Fixed) // Fixed mode (does not scroll with content)
      .onChange((index: number) => { // Tab switching event
        this.vm.curIndex = index; // Update router management state
        this.currentTabIndex = index; // Update current index
      });
    }
    // Navigation bar title configuration
    .title({ 
      builder: this.titleBuilder(TAB_LIST[this.vm.curIndex]), // Dynamic title
      height: 92 // Title bar height
    })
    .mode(NavigationMode.Stack) // Stack navigation mode
  }

  @Builder
  titleBuilder(title: TabListItem) {
    Row() {
      Text(title.label) // Display the current module name
        .fontWeight(FontWeight.Bold) // Bold
        .fontColor('rgba(0,0,0,0.90)') // Font color
        .fontSize($r('app.float.navigation_title_font_size')) // Font size (resource reference)
        .margin({ left: 16, top: 36 }) // Margin
        .height(56) // Height
    }
    .justifyContent(FlexAlign.Start) // Left-aligned
    .backgroundColor(title.titleBackgroundColor) // Background color (dynamic configuration)
    .width('100%') // Full width
    .height('100%') // Full height
    .alignItems(VerticalAlign.Bottom) // Vertically bottom-aligned
  }

  // --------------------------
  // Bottom navigation bar item builder
  // --------------------------
  @Builder
  tabBarBuilder(item: TabListItem, index: number) {
    Column() { // Vertical layout
      // Dynamically display selected/unselected icons
      Image(this.vm.curIndex === index ? item.iconChecked : item.icon)
        .width($r('app.float.navigation_image_size')) // Icon width (resource reference)
        .height($r('app.float.navigation_image_size')) // Icon height
        .margin({ top: $r('app.string.margin_xs') }); // Top margin

      // Tab text label
      Text(item.label)
        .fontColor(this.vm.curIndex === index ? 
          $r('app.color.icon_color_highlight') : // Selected highlight color
          $r('app.color.icon_color_level2')) // Unselected default color
        .fontSize($r('app.float.navigation_navi_size')) // Font size
        .height(14) // Fixed height
        .margin({ 
          top: $r('app.string.margin_xs'), 
          bottom: $r('app.string.margin_xs') 
        });
    }
    .height(80) // Fixed height
    .width('100%') // Full width
    .justifyContent(FlexAlign.Start); // Top-aligned
  }
}
Enter fullscreen mode Exit fullscreen mode

IV. Schedule Function Module and Implementation Process

UI Structure Design of the Schedule Module

The schedule consists of the following core components:

Main Schedule View: Supports switching between monthly and weekly views, with highlighting of today's schedule.

Schedule Details Page: Displays time, location, participants, and reminder settings.

Add/Edit Page: Provides form input, map location selection, and attachment upload functionality.

Functional Implementation Steps

(1) Creation and Implementation Code of ScheduleView.ets File:

① Create a scenes directory under the root directory OfficeAttendance.

② Create a schedule module (HSP dynamic sharing) under the scenes directory.

③ Code Implementation of ScheduleView.ets:

typescript
import { DataPreferencesUtils } from '@ohos_agcit/office_attendance_common_lib';
import { DateElement } from '@ohos_agcit/office_attendance_component_lib/src/main/ets/types/DateElement';

@ComponentV2
export struct ScheduleTitle {
  @Param selectedMonth: DateElement = new DateElement(0, 0, 0); // Currently selected month
  @Param onChange: (selected: DateElement) => void = (selected: DateElement) => {
  } // Callback function for month change
  @Param onLunarChange: () => void = () => {
  } // Callback function for lunar calendar display setting change

  // Switch to the previous month
  private goBeforeMonth() {
    this.selectedMonth.year =
      this.selectedMonth.month - 1 >= 0 ? this.selectedMonth.year : this.selectedMonth.year - 1;
    this.selectedMonth.month = this.selectedMonth.month - 1 >= 0 ? this.selectedMonth.month - 1 : 11;
    this.onChange(this.selectedMonth);
  }

  // Switch to the next month
  private goAfterMonth() {
    this.selectedMonth.year =
      this.selectedMonth.month + 1 < 12 ? this.selectedMonth.year : this.selectedMonth.year + 1;
    this.selectedMonth.month = this.selectedMonth.month + 1 < 12 ? this.selectedMonth.month + 1 : 0;
    this.onChange(this.selectedMonth);
  }

  // Check if lunar calendar display is enabled
  isSettingLunar() {
    return DataPreferencesUtils.LUNAR_SETTING_ON ===
    DataPreferencesUtils.getSync(DataPreferencesUtils.LUNAR_SETTING, DataPreferencesUtils.LUNAR_SETTING_ON)
  }

  @Builder
  settingBuilder() {
    Column() {
      Row() {
        Row() {
          Text($r('app.string.schedule_lc_desc')) // Display lunar calendar setting description text
        }.alignItems(VerticalAlign.Center)
        .justifyContent(FlexAlign.Start)
        .width('60%')

        Row() {
          // Display checkmark icon if lunar calendar display is enabled
          if (this.isSettingLunar()) {
            Image($r('app.media.ic_check_mask')).width(24).height(16)
          }
        }.alignItems(VerticalAlign.Center)
        .justifyContent(FlexAlign.End)
        .layoutWeight(1)
      }.alignItems(VerticalAlign.Center)
      .justifyContent(FlexAlign.Center)
      .onClick(() => {
        // Toggle lunar calendar display setting on click
        if (this.isSettingLunar()) {
          DataPreferencesUtils.putSync(DataPreferencesUtils.LUNAR_SETTING, DataPreferencesUtils.LUNAR_SETTING_OFF)
        } else {
          DataPreferencesUtils.putSync(DataPreferencesUtils.LUNAR_SETTING, DataPreferencesUtils.LUNAR_SETTING_ON)
        }
        this.onLunarChange();
      })
    }.height(45)
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .width(240)
    .margin({ top: 10, bottom: 10 })
  }

  build() {
    Row() {
      // Left side: Month navigation area
      Row() {
        // Previous month button
        Row() {
          Image($r('app.media.ic_public_small_left'))
            .id('pre_month')
            .height(20)
            .width(12)
        }
        .margin({ left: 5 })
        .height('100%')
        .aspectRatio(1)
        .justifyContent(FlexAlign.Center)
        .onClick(() => {
          this.goBeforeMonth();
        })
        Column().width('6%') // Spacing
        // Display current year and month
        Text(`${this.selectedMonth.year}-${this.selectedMonth.month + 1 >= 10 ? '' : '0'}${this.selectedMonth.month +
          1}`)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
          .opacity(0.9)
        Column().width('6%') // Spacing
        // Next month button
        Row() {
          Image($r('app.media.ic_public_small_right'))
            .id('next_month')
            .height(20)
            .width(12)
        }
        .margin({ left: 5 })
        .height('100%')
        .aspectRatio(1)
        .justifyContent(FlexAlign.Center)
        .onClick(() => {
          this.goAfterMonth();
        })
        .alignItems(VerticalAlign.Center)
        .justifyContent(FlexAlign.Start)
      }.width('50%')
      .alignItems(VerticalAlign.Center)
      .justifyContent(FlexAlign.Start)

      // Right side: Settings button
      Row() {
        Image($r('app.media.ic_ellipsis'))
          .width(20)
          .height(12)
          .bindMenu(this.settingBuilder) // Bind settings menu
      }
      .width('50%')
      .alignItems(VerticalAlign.Center)
      .justifyContent(FlexAlign.End)
    }.width('100%')
    .height(24)
  }
}

// Description: Calendar - Date
// Implementation of CalenderView.ets
import { i18n } from '@kit.LocalizationKit';
import { CommonConstants, DateUtils } from '@ohos_agcit/office_attendance_common_lib';
import { DateElement } from '../types/DateElement';

@ObservedV2
class ShowDays {
  @Trace showDays: DateElement [][] = [];  // Monthly view date data (2D array)
  @Trace showWeekDays: DateElement [][] = [];  // Weekly view date data
}

@ComponentV2
export struct CalenderView {
  // Component parameters
  @Param isShowLunar: boolean = true;  // Whether to display lunar calendar
  @Param fontSize: Resource = $r('app.float.calendar_title_font_size')  // Font size resource
  @Param titleFontOpacity: Resource = $r('app.float.calendar_title_font_opacity')  // Title opacity
  @Param contentFontOpacity: Resource = $r('app.float.calendar_content_font_opacity')  // Content opacity
  @Param weekendFontOpacity: Resource = $r('app.float.calendar_weekend_font_opacity')  // Weekend opacity
  @Param beforeDateFontOpacity: Resource = $r('app.float.calendar_before_date_font_opacity')  // Non-current month date opacity
  @Param todayFontOpacity: Resource = $r('app.float.calendar_today_font_opacity')  // Today's opacity
  readonly TIME_DEF_VALUE = -1;  // Default time value

  // Localized calendar instance
  private calendar = i18n.getCalendar(CommonConstants.EN_LOCALE);
  @Local today: number = this.calendar.get(CommonConstants.FIELD_DAY);  // Current day
  @Local currentMonth: number = this.calendar.get(CommonConstants.FIELD_MONTH);  // Current month
  @Local currentYear: number = this.calendar.get(CommonConstants.FIELD_YEAR);  // Current year

  // Component state
  @Param selectedDay: DateElement = new DateElement(0, 0, this.TIME_DEF_VALUE);  // Selected date
  @Event onChangeDay: (val: DateElement) => void = (val: DateElement) => {};  // Date change event
  @Param showMonth: DateElement = new DateElement(this.TIME_DEF_VALUE, this.TIME_DEF_VALUE, this.TIME_DEF_VALUE);  // Displayed month
  @Event onChangeMonth: (showMonth: DateElement) => void = (showMonth: DateElement) => {};  // Month change event
  @Param showDotDate: Array<string> = []  // Dates to display dots
  @Local viewType: number = CommonConstants.MONTH_VIEW;  // View type (month/week)
  @Event onChangeViewType: (showType: number) => void = (showType: number) => {};  // View switch event
  @Param hasScheduleDates: Array<string> = [];  // List of dates with schedules
  showDays: ShowDays = new ShowDays();  // Date data instance

  // Monitor month and selected day changes
  @Monitor('showMonth','selectedDay')
  onSelectedMonthChange(monitor: IMonitor) {
    this.refresh();
  }

  // Check if it's a weekend
  isWeekends(index: number) {
    return index === CommonConstants.COMMON_ZERO || index === CommonConstants.COMMON_SIX;
  }

  // Refresh calendar data
  refresh() {
    this.init();
    // Set calendar to the first day of the displayed month
    this.calendar.set(this.showMonth.year, this.showMonth.month, 0);

    // Calculate previous and next month information
    let preMonth = this.showMonth.month - 1 >= 0 ? this.showMonth.month - 1 : 11;
    let preMonthYear = this.showMonth.month - 1 >= 0 ? this.showMonth.year : this.showMonth.year - 1;
    let preMonthDays: number = -this.calendar.compareDays(new Date(preMonthYear, preMonth, 0)) - 1;

    let nextMonth = this.showMonth.month + 1 < 12 ? this.showMonth.month + 1 : 0;
    let nextMonthYear = this.showMonth.month + 1 < 12 ? this.showMonth.year : this.showMonth.year + 1;
    let selectMonthDays: number = this.calendar.compareDays(new Date(nextMonthYear, nextMonth, 0));

    // Build date array
    let firstDayOfWeek = this.calendar.get(CommonConstants.FIELD_DAY_OF_WEEK);
    let weedDays: DateElement [] = [];
    let diffDays = firstDayOfWeek < 7 ? firstDayOfWeek : 0;

    // Fill previous month's dates
    for (let i = 1; i <= diffDays; i++) {
      weedDays.push(new DateElement(preMonthYear, preMonth, preMonthDays - diffDays + i));
    }

    // Fill current month's dates
    for (let i = 1; i <= selectMonthDays; i++) {
      if (weedDays.length === 7) {
        this.showDays.showDays.push(weedDays);
        weedDays = [];
      }
      weedDays.push(new DateElement(this.showMonth.year, this.showMonth.month, i));
    }

    // Fill next month's dates
    if (weedDays.length < 7) {
      let diffDays = 7 - weedDays.length;
      for (let i = 0; i < diffDays; i++) {
        weedDays.push(new DateElement(nextMonthYear, nextMonth, i + 1));
      }
    }
    this.showDays.showDays.push(weedDays);

    // Synchronize selected date state
    if (this.selectedDay.year !== this.showMonth.year || this.selectedDay.month !== this.showMonth.month) {
      this.selectedDay.year = this.showMonth.year;
      this.selectedDay.month = this.showMonth.month;
      this.onChangeDay(this.selectedDay);
    }
    if (this.selectedDay.day === this.TIME_DEF_VALUE) {
      this.selectedDay.day = this.today;
      this.onChangeDay(this.selectedDay);
    }
    this.setWeedViewData()
  }

  // Set weekly view data
  setWeedViewData() {
    this.showDays.showDays.forEach(week => {
      week.forEach(day => {
        if (this.isSelectedDay(day)) {
          this.showDays.showWeekDays = [week];  // Find the week containing the selected day
        }
      })
    })
  }

  // Initialize month
  init() {
    this.showDays.showDays = [];
    if (this.showMonth.year === this.TIME_DEF_VALUE) {
      this.showMonth.year = this.currentYear;
      this.showMonth.month = this.currentMonth;
      this.onChangeMonth(this.showMonth);
    }
  }

  // Lifecycle: Component is about to appear
  aboutToAppear() {
    this.refresh();
  }

  // Check if it's today
  private isToday(day: DateElement): boolean {
    return this.today === day.day && this.currentMonth === day.month &&
      this.currentYear === day.year;
  }

  // Check if it's a past date
  isBeforeDay(day: DateElement) {
    return this.calendar.compareDays(new Date(day.year, day.month, day.day)) < 0;
  }

  // Zero-padding formatting
  appendZero(num: number): string {
    return num < 10 ? '0' + num : '' + num;
  }

  // Check if it's the selected day
  private isSelectedDay(day: DateElement): boolean {
    return this.selectedDay.isEquals(day);
  }

  // Check if it's the current month
  isCurrentMonth(day: DateElement) {
    return this.showMonth.year === day.year && this.showMonth.month === day.month;
  }

  // Switch month/week by gesture
  private changeMonthByGesture(e: GestureEvent) {
    if (e.offsetX > 10) {  // Swipe left
      if (this.viewType === CommonConstants.MONTH_VIEW) {
        // Switch to previous month in monthly view
        this.showMonth.year = this.showMonth.month - 1 < 0 ? this.showMonth.year - 1 : this.showMonth.year;
        this.showMonth.month = this.showMonth.month - 1 < 0 ? 11 : this.showMonth.month - 1;
        this.onChangeMonth(this.showMonth);
      } else {
        // Switch to previous week in weekly view
        let selectedDate = new Date(this.selectedDay.year, this.selectedDay.month, this.selectedDay.day);
        selectedDate = new Date(selectedDate.getTime() - CommonConstants.SEVEN_DAY);
        this.changeSelectDay(new DateElement(selectedDate.getFullYear(),
          selectedDate.getMonth(),
          selectedDate.getDate()))
      }
    } else if (e.offsetX < -10) {  // Swipe right
      if (this.viewType === CommonConstants.MONTH_VIEW) {
        // Switch to next month in monthly view
        this.showMonth.year = this.showMonth.month + 1 < 12 ? this.showMonth.year : this.showMonth.year + 1;
        this.showMonth.month = this.showMonth.month + 1 < 12 ? this.showMonth.month + 1 : 0;
        this.onChangeMonth(this.showMonth);
      } else {
        // Switch to next week in weekly view
        let selectedDate = new Date(this.selectedDay.year, this.selectedDay.month, this.selectedDay.day);
        selectedDate = new Date(selectedDate.getTime() + CommonConstants.SEVEN_DAY);
        this.changeSelectDay(new DateElement(selectedDate.getFullYear(),
          selectedDate.getMonth(),
          selectedDate.getDate()))
      }
    }
  }

  // Change selected day
  changeSelectDay(day: DateElement) {
    this.selectedDay.year = day.year;
    this.selectedDay.month = day.month;
    this.selectedDay.day = day.day;
    this.onChangeDay(day);
    this.setWeedViewData();
    this.changeMonth(day);
  }

  // Switch month
  changeMonth(month: DateElement) {
    this.showMonth.year = month.year;
    this.showMonth.month = month.month;
    this.onChangeMonth(month);
  }

  // Check if there's a schedule
  isHasSchedule(day: DateElement) {
    let dayStr: string = day.getDateString();
    return this.hasScheduleDates.filter(t => {
      return t.indexOf(dayStr) > -1;
    }).length > 0;
  }

  build() {
    Column() {
      // Week title area
      Column() {
        Flex({ justifyContent: FlexAlign.SpaceBetween }) {
          ForEach(CommonConstants.WEEK_TITLE, (str: Resource, index: number) => {
            Column() {
              Text(str)  // Display weekday title
                .fontSize(this.fontSize)
                .fontWeight(FontWeight.Regular)
                .textAlign(TextAlign.Center)
                .opacity(this.titleFontOpacity)
            }
            .justifyContent(FlexAlign.Center)
            .width($r('app.float.content_size'))
            .height($r('app.float.height_or_line_height'))
          })
        }
        .width(CommonConstants.FULL_PERCENT)
        .borderRadius($r('app.float.height_or_line_height'))
        .margin({
          bottom: $r('app.float.day_button_radius'),
          left: CommonConstants.COMMON_MARGIN,
          right: CommonConstants.COMMON_MARGIN
        })
      }

      // Date body area
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(this.viewType === CommonConstants.MONTH_VIEW ? this.showDays.showDays : this.showDays.showWeekDays,
          (weekDays: DateElement []) => {
            Flex({ justifyContent: FlexAlign.SpaceBetween }) {
              ForEach(weekDays, (day: DateElement, index: number) => {
                Column() {
                  // Solar date
                  Text(`${day.day}`)
                    .fontSize(this.fontSize)
                    .fontWeight(FontWeight.Medium)
                    .opacity(this.isToday(day) ? this.todayFontOpacity :  // Today's opacity
                      this.isWeekends(index) ? this.weekendFontOpacity :  // Weekend opacity
                        this.isCurrentMonth(day) ? this.contentFontOpacity : this.beforeDateFontOpacity)  // Non-current month opacity
                    .fontColor(this.isToday(day) && !this.isSelectedDay(day) ? $r('app.color.start_window_background') :
                    $r('app.color.black'))

                  // Lunar date
                  Text(DateUtils.getCurrentLunarDayDesc(day.year, day.month, day.day))
                    .fontSize(this.fontSize)
                    .visibility(this.isShowLunar ? Visibility.Visible : Visibility.None)
                    .opacity(this.isToday(day) ? this.todayFontOpacity :  // Opacity settings same as above
                      this.isWeekends(index) ? this.weekendFontOpacity :
                        this.isCurrentMonth(day) ? this.contentFontOpacity : this.beforeDateFontOpacity)
                    .fontColor(this.isToday(day) && !this.isSelectedDay(day) ? $r('app.color.start_window_background') :
                    $r('app.color.black'))

                  // Schedule dot marker
                  Column()
                    .width(4)
                    .height(4)
                    .borderRadius(2)
                    .backgroundColor(this.isSelectedDay(day) ? $r('app.color.sys_default_blue') :  // Selected state color
                      this.isToday(day) ? Color.White : $r('app.color.sys_default_blue'))  // Today's color
                    .visibility(this.isHasSchedule(day) ? Visibility.Visible : Visibility.None)  // Display when there's a schedule
                }
                .onClick(() => {
                  this.changeSelectDay(day);  // Select date on click
                })
                // Background style
                .backgroundColor(this.isToday(day) && !this.isSelectedDay(day) ? $r('app.color.sys_default_blue') :
                Color.White)
                .borderRadius($r('app.float.schedule_border_radius'))
                .borderColor(this.isSelectedDay(day) ? $r('app.color.sys_default_blue') : Color.White)  // Selected border
                .borderWidth(this.isSelectedDay(day) ? 1 : 0)
                .justifyContent(FlexAlign.Center)
                .width($r('app.float.content_size'))
                .height($r('app.float.content_size'))
              })
            }
            .width('100%')
            .margin({ top: '6vp' })
          })
      }
      .width('100%')
      // Add horizontal swipe gesture
      .gesture(PanGesture({ fingers: 1, direction: PanDirection.Horizontal, distance: 5 })
        .onActionEnd((e: GestureEvent) => {
          this.changeMonthByGesture(e);  // Switch month/week by gesture
        })
      )

      Column().height('2%')  // Spacing area

      // View switch buttons
      Column() {
        // Expand monthly view button
        Image($r('app.media.ic_chevron_up'))
          .width(64)
          .height(16)
          .visibility(this.viewType === CommonConstants.MONTH_VIEW ? Visibility.Visible : Visibility.None)
          .onClick(() => {
            this.viewType = CommonConstants.WEEK_VIEW
            this.onChangeViewType(this.viewType)
          })
        // Collapse weekly view button  
        Image($r('app.media.ic_chevron_down'))
          .width(64)
          .height(16)
          .visibility(this.viewType === CommonConstants.WEEK_VIEW ? Visibility.Visible : Visibility.None)
          .onClick(() => {
            this.viewType = CommonConstants.MONTH_VIEW
            this.onChangeViewType(this.viewType)
          })
      }.height(16)
      .width('100%')
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Comments 0 total

    Add comment