#include "womenhealth/include/WomenHealthModel.h"
+ #include "wearable_log.h"
+
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- using namespace OHOS;
-
- WomenHealthModel::WomenHealthModel()
- {
- }
-
- WomenHealthModel::~WomenHealthModel()
- {
- }
+ /*
+- 经期默认时长:5-6 天(用户可在该范围选择具体天数)
+- 排卵期默认计算:基于 28 天标准周期,自动推算为 10 天(排卵日及前后各 4-5 天)
+- 周期默认值:28 天(示例:9 月 3 日开始经期,下次预测经期为 10 月 1 日,即 9 月 3 日加 28 天)
- WomenHealthModel *WomenHealthModel::GetInstance(void)
+ */
+
+ namespace OHOS
{
- static WomenHealthModel callLogModel;
- return &callLogModel;
- }
-
- #ifdef __cplusplus
- }
- #endif
+
+ WomenHealthModel::WomenHealthModel()
+ {
+ // 初始化所有日期为0
+ for (int i = 0; i < 3; i++) {
+ menstrualStartDate[i] = {0, 0, 0};
+ menstrualEndDate[i] = {0, 0, 0};
+ ovulationStartDate[i] = {0, 0, 0};
+ ovulationEndDate[i] = {0, 0, 0};
+ }
+ }
+
+ WomenHealthModel::~WomenHealthModel(){}
+
+ WomenHealthModel *WomenHealthModel::GetInstance(void)
+ {
+ static WomenHealthModel instance;
+ return &instance;
+ }
+
+ //获取第一个月的经期开始时间
+ TimeObj WomenHealthModel::GetMenstrualStartDate()
+ {
+ return menstrualStartDate[0];
+ }
+ //获取第一个月的经期结束时间
+ TimeObj WomenHealthModel::GetMenstrualEndDate()
+ {
+ return menstrualEndDate[0];
+ }
+ //获取第一个月的排卵期开始时间
+ TimeObj WomenHealthModel::GetOvulationStartDate()
+ {
+ return ovulationStartDate[0];
+ }
+ //获取第一个月的排卵期结束时间
+ TimeObj WomenHealthModel::GetOvulationEndDate()
+ {
+ return ovulationEndDate[0];
+ }
+
+ /**
+ * @brief 比较两个日期
+ * @return <0表示date1在date2前,0表示相等,>0表示date1在date2后
+ */
+ int WomenHealthModel::CompareDate(int year1, int month1, int day1, int year2, int month2, int day2)
+ {
+ if (year1 != year2) return year1 - year2;
+ if (month1 != month2) return month1 - month2;
+ return day1 - day2;
+ }
+
+ /**
+ * @brief 给日期加上指定天数
+ * @param date 原始日期
+ * @param days 要加的天数
+ * @return 新日期
+ */
+ TimeObj WomenHealthModel::AddDays(const TimeObj &date, int days)
+ {
+ TimeObj result = date;
+ result.day += days;
+
+ // 简单的日期计算(可以优化)
+ while (result.day > 31) {
+ result.day -= 30; // 简化:每月按30天计算
+ result.month++;
+ if (result.month > 12) {
+ result.month = 1;
+ result.year++;
+ }
+ }
+
+ while (result.day < 1) {
+ result.month--;
+ if (result.month < 1) {
+ result.month = 12;
+ result.year--;
+ }
+ result.day += 30; // 简化:每月按30天计算
+ }
+
+ return result;
+ }
+
+ /**
+ * @brief 用户设置经期开始日期
+ * @param startDate 经期开始日期
+ *
+ * 根据开始日期自动计算:
+ * 1. 经期结束时间 = 开始时间 + menstrualCycle天
+ * 2. 排卵期开始时间 = 经期结束时间 + menstrualOvulationDays天
+ * 3. 排卵期结束时间 = 排卵期开始时间 + ovulationCycle天
+ * 4. 预测下次经期 = 本次开始 + totalCycle天(28天)
+ */
+ void WomenHealthModel::SetMenstrualStartDate(const TimeObj &startDate)
+ {
+ //设置这个月的经期开始时间
+ menstrualStartDate[0] = startDate;
+
+ //经期结束时间 = 开始时间 + menstrualCycle天(默认5天)
+ menstrualEndDate[0] = AddDays(menstrualStartDate[0], menstrualCycle);
+
+ //排卵期开始时间 = 经期结束时间 + menstrualOvulationDays天(默认5天)
+ ovulationStartDate[0] = AddDays(menstrualEndDate[0], menstrualOvulationDays);
+
+ //排卵期结束时间 = 排卵期开始时间 + ovulationCycle天(默认10天)
+ ovulationEndDate[0] = AddDays(ovulationStartDate[0], ovulationCycle);
+
+ //预测下次经期 = 本次开始 + totalCycle天(默认28天)
+ menstrualStartDate[1] = AddDays(menstrualStartDate[0], totalCycle);
+ menstrualEndDate[1] = AddDays(menstrualStartDate[1], menstrualCycle);
+ ovulationStartDate[1] = AddDays(menstrualEndDate[1], menstrualOvulationDays);
+ ovulationEndDate[1] = AddDays(ovulationStartDate[1], ovulationCycle);
+
+ //预测下下次经期 = 下次开始 + totalCycle天
+ menstrualStartDate[2] = AddDays(menstrualStartDate[1], totalCycle);
+ menstrualEndDate[2] = AddDays(menstrualStartDate[2], menstrualCycle);
+ ovulationStartDate[2] = AddDays(menstrualEndDate[2], menstrualOvulationDays);
+ ovulationEndDate[2] = AddDays(ovulationStartDate[2], ovulationCycle);
+
+ //打印所有时间
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "=== 本次月经 ===");
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "经期开始: %d-%d-%d", menstrualStartDate[0].year, menstrualStartDate[0].month, menstrualStartDate[0].day);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "经期结束: %d-%d-%d", menstrualEndDate[0].year, menstrualEndDate[0].month, menstrualEndDate[0].day);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "排卵期开始: %d-%d-%d", ovulationStartDate[0].year, ovulationStartDate[0].month, ovulationStartDate[0].day);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "排卵期结束: %d-%d-%d", ovulationEndDate[0].year, ovulationEndDate[0].month, ovulationEndDate[0].day);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "=== 预测下次周期 ===");
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "预测经期: %d-%d-%d 至 %d-%d-%d", menstrualStartDate[1].year, menstrualStartDate[1].month, menstrualStartDate[1].day, menstrualEndDate[1].year, menstrualEndDate[1].month, menstrualEndDate[1].day);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "预测排卵期: %d-%d-%d 至 %d-%d-%d", ovulationStartDate[1].year, ovulationStartDate[1].month, ovulationStartDate[1].day, ovulationEndDate[1].year, ovulationEndDate[1].month, ovulationEndDate[1].day);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "=== 预测下下次周期 ===");
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "预测经期: %d-%d-%d 至 %d-%d-%d", menstrualStartDate[2].year, menstrualStartDate[2].month, menstrualStartDate[2].day, menstrualEndDate[2].year, menstrualEndDate[2].month, menstrualEndDate[2].day);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "预测排卵期: %d-%d-%d 至 %d-%d-%d", ovulationStartDate[2].year, ovulationStartDate[2].month, ovulationStartDate[2].day, ovulationEndDate[2].year, ovulationEndDate[2].month, ovulationEndDate[2].day);
+ }
+
+ int WomenHealthModel::TimeDiffInMinutes(const TimeObj &startDate, const TimeObj &endDate)
+ {
+ //计算差值天数
+ int diffDays = (endDate.year - startDate.year) * 365 + (endDate.month - startDate.month) * 30 + (endDate.day - startDate.day);
+ return diffDays;
+ }
+
+ /**
+ * @brief 用户设置经期结束日期
+ * @param endDate 经期结束日期
+ *
+ * 根据开始和结束日期重新计算周期长度
+ */
+ void WomenHealthModel::SetMenstrualEndDate(const TimeObj &endDate)
+ {
+ menstrualEndDate[0] = endDate;
+ //计算经期持续天数
+ int diffDays = TimeDiffInMinutes(menstrualStartDate[0], menstrualEndDate[0]);
+ if (diffDays > 0) {
+ menstrualCycle = diffDays;
+ }
+ //重新计算排卵期和预测经期
+ SetMenstrualStartDate(menstrualStartDate[0]);
+ }
+
+ /**
+ * @brief 判断某天是否在月经期内(已记录的)
+ * @return true表示在月经期内
+ */
+ bool WomenHealthModel::IsInMenstrualPeriod(int year, int month, int day)
+ {
+ // 检查第一个月经期(已记录的)
+ if (menstrualStartDate[0].year == 0) {
+ return false; // 还没有设置数据
+ }
+
+ int cmpStart = CompareDate(year, month, day,
+ menstrualStartDate[0].year, menstrualStartDate[0].month, menstrualStartDate[0].day);
+ int cmpEnd = CompareDate(year, month, day,
+ menstrualEndDate[0].year, menstrualEndDate[0].month, menstrualEndDate[0].day);
+
+ // 在开始和结束之间
+ return (cmpStart >= 0 && cmpEnd <= 0);
+ }
+
+ /**
+ * @brief 判断某天是否在排卵期内
+ * @return true表示在排卵期内
+ */
+ bool WomenHealthModel::IsInOvulationPeriod(int year, int month, int day)
+ {
+ // 检查排卵期
+ if (ovulationStartDate[0].year == 0) {
+ return false; // 还没有设置数据
+ }
+
+ int cmpStart = CompareDate(year, month, day,
+ ovulationStartDate[0].year, ovulationStartDate[0].month, ovulationStartDate[0].day);
+ int cmpEnd = CompareDate(year, month, day,
+ ovulationEndDate[0].year, ovulationEndDate[0].month, ovulationEndDate[0].day);
+
+ // 在开始和结束之间
+ return (cmpStart >= 0 && cmpEnd <= 0);
+ }
+
+ /**
+ * @brief 判断某天是否在预测经期内
+ * @return true表示在预测经期内
+ */
+ bool WomenHealthModel::IsInPredictPeriod(int year, int month, int day)
+ {
+ // 检查预测的月经期(索引1和2)
+ for (int i = 1; i < 3; i++) {
+ if (menstrualStartDate[i].year == 0) {
+ continue;
+ }
+
+ int cmpStart = CompareDate(year, month, day,
+ menstrualStartDate[i].year, menstrualStartDate[i].month, menstrualStartDate[i].day);
+ int cmpEnd = CompareDate(year, month, day,
+ menstrualEndDate[i].year, menstrualEndDate[i].month, menstrualEndDate[i].day);
+
+ // 在预测的开始和结束之间
+ if (cmpStart >= 0 && cmpEnd <= 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @brief 判断某天是否在预测排卵期内(下次和下下次的排卵期)
+ * @return true表示在预测排卵期内
+ */
+ bool WomenHealthModel::IsInPredictOvulationPeriod(int year, int month, int day)
+ {
+ // 检查预测的排卵期(索引1和2)
+ for (int i = 1; i < 3; i++) {
+ if (ovulationStartDate[i].year == 0) {
+ continue;
+ }
+
+ int cmpStart = CompareDate(year, month, day,
+ ovulationStartDate[i].year, ovulationStartDate[i].month, ovulationStartDate[i].day);
+ int cmpEnd = CompareDate(year, month, day,
+ ovulationEndDate[i].year, ovulationEndDate[i].month, ovulationEndDate[i].day);
+
+ // 在预测的排卵期开始和结束之间
+ if (cmpStart >= 0 && cmpEnd <= 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @brief 获取经期开始的年月
+ * @param year 输出参数:年份
+ * @param month 输出参数:月份
+ */
+ void WomenHealthModel::GetMenstrualStartYearMonth(int &year, int &month)
+ {
+ year = menstrualStartDate[0].year;
+ month = menstrualStartDate[0].month;
+ }
+
+ /**
+ * @brief 获取预测的最后一个月(第3次预测经期的月份)
+ * @param year 输出参数:年份
+ * @param month 输出参数:月份
+ */
+ void WomenHealthModel::GetLastPredictYearMonth(int &year, int &month)
+ {
+ // 第3次预测(索引2)的经期结束月份
+ if (menstrualEndDate[2].year != 0) {
+ year = menstrualEndDate[2].year;
+ month = menstrualEndDate[2].month;
+ } else {
+ year = 0;
+ month = 0;
+ }
+ }
+
+ /**
+ * @brief 检查是否已设置经期数据
+ * @return true表示已设置,false表示未设置
+ */
+ bool WomenHealthModel::HasMenstrualData()
+ {
+ return (menstrualStartDate[0].year != 0);
+ }
+
+
+}
\ No newline at end of file
#include "NativeAbility.h"
#include "ConfigModel.h"
#include "languagecode/LanguageInit.h"
- namespace OHOS
- {
-
- static WomenHealthView *g_pMainView_WomenHealthView = nullptr;
-
- static char *dial_id_str[] = {"7", "8", "9", "C", "%", "4", "5", "6", "÷", "x", "1", "2", "3", "-", "+", "", "0", ".", "=", ""};
-
- static constexpr int16_t PULLUP_ANIMATOR_VIEW_X = 213;
- static constexpr int16_t PULLUP_ANIMATOR_VIEW_Y = 380;
- static constexpr int16_t PULLUP_ANIMATOR_VIEW_W = 40;
- static constexpr int16_t PULLUP_ANIMATOR_VIEW_H = 40;
- static constexpr int16_t PULLUP_ANIMATOR_VIEW_NUM_FRAMES = 70;
- static constexpr int16_t PULLUP_ANIMATOR_VIEW_TIME = 100;
-
- static constexpr const char *PullUpPaths = "/user/res/weather/PullUp/PullUp_";
-
- static constexpr const char *stepPaths = "/user/res/step/";
-
- WomenHealthView::WomenHealthView()
- {
- g_pMainView_WomenHealthView = this;
- }
-
- WomenHealthView::~WomenHealthView()
- {
- g_pMainView_WomenHealthView = nullptr;
- if (group)
- {
- group->RemoveAll();
-
- if (mon_label)
- {
- delete mon_label;
- mon_label = nullptr;
- }
-
- if (imageview)
- {
- delete imageview;
- imageview = nullptr;
- }
-
- for (int i = 0; i < 7; i++)
- {
- if (week_label[i])
- {
- delete week_label[i];
- week_label[i] = nullptr;
- }
- }
-
- for (int i = 0; i < 31; i++)
- {
- if (day_label[i])
- {
- delete day_label[i];
- day_label[i] = nullptr;
- }
- }
-
- if (PullUpAnimatorView != nullptr)
- {
- PullUpAnimatorView->Stop();
- delete PullUpAnimatorView;
- PullUpAnimatorView = nullptr;
- }
-
- FreeImageInfoArray(PullUpimageAnimator_imageInfo, PullUpPaths, PULLUP_ANIMATOR_VIEW_NUM_FRAMES);
-
- delete group;
- group = nullptr;
- }
-
- g_pMainView_WomenHealthView = nullptr;
- }
-
- WomenHealthView *WomenHealthView::GetInstance()
- {
- return g_pMainView_WomenHealthView;
- }
- static char *week_Z_str[] = {"一", "二", "三", "四", "五", "六", "日"};
- static char *week_E_str[] = {"MON", "THE", "WED", "THU", "FRI", "SAT", "SUN"};
-
- bool WomenHealthView::updata_label(int8_t type)
- {
- for (int i = 0; i < 31; i++)
- {
- day_label[i]->SetVisible(false);
- }
- imageview->SetVisible(false);
-
- if (type == 1)
- {
- show_m--;
- if (show_m == 0)
- {
- show_m = 12;
- show_y--;
- }
- }
- else
- {
- show_m++;
- if (show_m == 13)
- {
- show_m = 1;
- show_y++;
- }
- }
- uint8_t mon_max_day = 0;
- uint8_t is_run = 0;
- if (show_y % 4 == 0 && show_y % 100 != 0 || show_y % 400 == 0)
- {
- is_run = 1;
- }
-
- if (show_m == 2)
- {
- mon_max_day = 28 + is_run;
- }
- else if (show_m == 1 || show_m == 3 || show_m == 5 || show_m == 7 || show_m == 8 || show_m == 10 || show_m == 12)
- {
- mon_max_day = 31;
- }
- else
- {
- mon_max_day = 30;
- }
-
- int y = show_y;
- int m = show_m;
- int week = 0;
- {
- if (m == 1 || m == 2)
- {
- m += 12;
- y--;
- }
- week = (1 + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
- }
-
- char mon[20] = {0};
- sprintf(mon, "%d.%d", show_y, show_m);
- mon_label->SetText(mon);
-
- for (int i = 0; i < mon_max_day; i++)
- {
- int x = ((i + week) % 7);
- int y = ((i + week) / 7);
- day_label[i]->SetPosition(58 + 50 * x, 140 + 44 * y); // widget's left position and top position
- char day[5];
- sprintf(day, "%d", i + 1);
- ;
- day_label[i]->SetText(day);
- time_t currtime;
- time(&currtime);
- struct tm *timeinfo;
- timeinfo = localtime(&currtime);
- int y_ = timeinfo->tm_year + 1900;
- int m_ = timeinfo->tm_mon + 1;
- int d_ = timeinfo->tm_mday;
- if (i + 1 == d_ && show_y == y_ && show_m == m_)
- {
- imageview->SetPosition(58 + 50 * x, 140 + 44 * y);
- imageview->SetVisible(true);
- }
- day_label[i]->SetVisible(true);
- }
- RootView::GetInstance()->Invalidate();
- return true;
- }
- bool WomenHealthView::Label_init()
- {
- // Add Label to App
- uint8_t mon_max_day = 0;
-
- uint8_t is_run = 0;
- time_t currtime;
- time(&currtime);
- struct tm *timeinfo;
- timeinfo = localtime(&currtime);
- int y = timeinfo->tm_year + 1900;
- int m = timeinfo->tm_mon + 1;
- int d = timeinfo->tm_mday;
- int h = timeinfo->tm_hour;
- int min = timeinfo->tm_min;
- int week = timeinfo->tm_wday;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%d,%d,%d,%d", y, m, d, week);
- if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0) //
- {
- is_run = 1;
- }
-
- if (m == 2)
- {
- mon_max_day = 28 + is_run;
- }
- else if (m == 1 ||
- m == 3 ||
- m == 5 ||
- m == 7 ||
- m == 8 ||
- m == 10 ||
- m == 12)
- {
- mon_max_day = 31;
- }
- else
- {
- mon_max_day = 30;
- }
-
- show_y = y;
- show_m = m;
- show_d = d;
-
- if (mon_label == nullptr)
- {
- mon_label = new UILabel();
- }
- mon_label->SetPosition(83, 50); // widget's left position and top position
- mon_label->Resize(300, 35); // widget's width and height
- mon_label->SetViewId("label1"); // Set style
- mon_label->SetStyle(STYLE_TEXT_FONT, 30);
-
- char mon[20] = {0};
- sprintf(mon, "%d.%d", y, m);
-
- if (m == 1 || m == 2)
- {
- m += 12;
- y--;
- }
- week = (1 + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7;
-
- mon_label->SetText(mon);
- mon_label->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
- mon_label->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
- mon_label->SetRollSpeed(0);
- mon_label->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
-
- // Add to rootView
- group->Add(mon_label);
-
- for (int i = 0; i < 7; i++)
- {
- if (week_label[i] == nullptr)
- {
- week_label[i] = new UILabel();
- }
- week_label[i]->SetPosition(58 + 50 * (i), 110); // widget's left position and top position
- week_label[i]->Resize(50, 30); // widget's width and height
- week_label[i]->SetViewId("label3"); // Set style
- week_label[i]->SetStyle(STYLE_TEXT_FONT, 20);
-
- if (ConfigModel::GetInstance()->GetLanguage() == ZH_CN)
- week_label[i]->SetText(week_Z_str[i]);
- else
- week_label[i]->SetText(week_E_str[i]);
-
- week_label[i]->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
- week_label[i]->SetTextColor(Color::GetColorFromRGBA(0x66, 0x66, 0x66, 0xff));
- week_label[i]->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
- week_label[i]->SetRollSpeed(0);
- week_label[i]->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 20);
-
- group->Add(week_label[i]);
- week_label[i]->SetVisible(true);
- }
- // Add Label to App
- if (imageview == nullptr)
- {
- imageview = new UIImageView();
- }
-
- imageview->SetPosition(58, 140);
- imageview->Resize(50, 50);
- imageview->SetViewId("image1");
- imageview->SetStyle(STYLE_BACKGROUND_COLOR, 0xffff5a36);
- imageview->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
- imageview->SetStyle(STYLE_BORDER_RADIUS, 100);
- group->Add(imageview);
- imageview->SetVisible(false);
-
- for (int i = 0; i < 31; i++)
- {
- if (day_label[i] == nullptr)
- {
- day_label[i] = new UILabel();
- }
-
- day_label[i]->SetPosition(58, 140); // widget's left position and top position
- day_label[i]->Resize(50, 44); // widget's width and height
- day_label[i]->SetViewId("label3"); // Set style
- day_label[i]->SetStyle(STYLE_TEXT_FONT, 20);
-
- day_label[i]->SetStyle(STYLE_BORDER_RADIUS, 100);
-
- day_label[i]->SetLineBreakMode(UILabel::LINE_BREAK_ELLIPSIS);
- day_label[i]->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
- day_label[i]->SetRollSpeed(0);
- day_label[i]->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
-
- group->Add(day_label[i]);
- day_label[i]->SetVisible(false);
- }
- for (int i = 0; i < mon_max_day; i++)
- {
- int x = ((i + week) % 7);
- int y = ((i + week) / 7);
- day_label[i]->SetPosition(58 + 50 * x, 140 + 44 * y); // widget's left position and top position
- char day[5];
- sprintf(day, "%d", i + 1);
- ;
- day_label[i]->SetText(day);
- if (i + 1 == d)
- {
- imageview->SetPosition(58 + 50 * x, 140 + 44 * y);
- imageview->SetVisible(true);
- }
- day_label[i]->SetVisible(true);
- }
-
- PullUpimageAnimator_imageInfo = CreateImageInfoArray(PullUpPaths, PULLUP_ANIMATOR_VIEW_NUM_FRAMES);
- if (PullUpAnimatorView == nullptr)
- {
- PullUpAnimatorView = new UIImageAnimatorView();
- if (PullUpAnimatorView == nullptr)
- {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "PullUpAnimatorView new fail");
- return false;
- }
- for (uint8_t i = 0; i < PULLUP_ANIMATOR_VIEW_NUM_FRAMES; i++)
- {
- // PullUpimageAnimatorInfo[i].imageInfo = ImageCacheManager::GetInstance().LoadSingleRes(PullUpPathsArray[i]);
- // PullUpimageAnimatorInfo[i].imageType = IMG_SRC_IMAGE_INFO;
- PullUpimageAnimatorInfo[i].imageInfo = PullUpimageAnimator_imageInfo[i];
- // PullUpPathsArray[i];
- PullUpimageAnimatorInfo[i].imageType = IMG_SRC_IMAGE_INFO;
- PullUpimageAnimatorInfo[i].pos.x = PULLUP_ANIMATOR_VIEW_X;
- PullUpimageAnimatorInfo[i].pos.y = PULLUP_ANIMATOR_VIEW_Y;
- PullUpimageAnimatorInfo[i].width = PULLUP_ANIMATOR_VIEW_W;
- PullUpimageAnimatorInfo[i].height = PULLUP_ANIMATOR_VIEW_H;
- }
- PullUpAnimatorView->SetPosition(PULLUP_ANIMATOR_VIEW_X, PULLUP_ANIMATOR_VIEW_Y, PULLUP_ANIMATOR_VIEW_W, PULLUP_ANIMATOR_VIEW_H);
- PullUpAnimatorView->SetStyle(STYLE_BACKGROUND_COLOR, 0xffffffff);
- PullUpAnimatorView->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
- PullUpAnimatorView->SetImageAnimatorSrc(PullUpimageAnimatorInfo, PULLUP_ANIMATOR_VIEW_NUM_FRAMES, PULLUP_ANIMATOR_VIEW_TIME);
- PullUpAnimatorView->SetRepeat(true);
- PullUpAnimatorView->Start();
- }
- group->Add(PullUpAnimatorView);
-
- return true;
- }
-
- void WomenHealthView::OnStart()
- {
- group = new UIViewGroup();
- if (group == nullptr)
- {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "CallingView::UIViewGroup::new group error");
- return;
- }
- group->SetPosition(0, 0);
- group->Resize(VERTICAL_RESOLUTION, HORIZONTAL_RESOLUTION); // 454 : value
- group->SetTouchable(true);
- group->SetDraggable(true);
- group->SetOnDragListener(presenter_);
- AddViewToRootContainer(group);
-
- if (!Label_init())
- {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "CallingView::UIViewGroup::new group error");
- return;
- }
- }
- }
\ No newline at end of file
+namespace OHOS
+{
+ // 全局静态指针,用于获取当前View实例
+ static WomenHealthView *g_pMainView_WomenHealthView = nullptr;
+
+ /**
+ * @brief 构造函数 - 创建女性健康界面对象
+ * 作用:初始化全局实例指针,让其他地方可以通过GetInstance()获取这个对象
+ */
+ WomenHealthView::WomenHealthView()
+ {
+ g_pMainView_WomenHealthView = this; // 保存当前对象指针到全局变量
+ }
+
+ /**
+ * @brief 析构函数 - 销毁女性健康界面对象
+ * 作用:清理所有分配的内存,防止内存泄漏
+ * 包括:主容器、月份标签、星期标签、日期标签、今日标记、动画等
+ */
+ WomenHealthView::~WomenHealthView()
+ {
+ g_pMainView_WomenHealthView = nullptr;
+ if (group) // 如果主容器存在
+ {
+ group->RemoveAll(); // 移除所有子控件
+
+ // 删除月份标签(如:"2024.10")
+ if (mon_label)
+ {
+ delete mon_label;
+ mon_label = nullptr;
+ }
+
+ // 删除31个日期颜色标记圆圈
+ for (int i = 0; i < 31; i++)
+ {
+ if (dayMarker[i])
+ {
+ delete dayMarker[i];
+ dayMarker[i] = nullptr;
+ }
+ }
+
+ // 删除7个星期标签(一、二、三、四、五、六、日)
+ for (int i = 0; i < 7; i++)
+ {
+ if (week_label[i])
+ {
+ delete week_label[i];
+ week_label[i] = nullptr;
+ }
+ }
+
+ // 删除31个日期标签(1-31号)
+ for (int i = 0; i < 31; i++)
+ {
+ if (day_label[i])
+ {
+ delete day_label[i];
+ day_label[i] = nullptr;
+ }
+ }
+
+ // 删除顶部标题"生理周期"
+ if (title_label)
+ {
+ delete title_label;
+ title_label = nullptr;
+ }
+
+ // 删除日期标签"X月X日 星期X"
+ if (mon_label)
+ {
+ delete mon_label;
+ mon_label = nullptr;
+ }
+
+ // 删除左右箭头按钮
+ if (left_arrow)
+ {
+ delete left_arrow;
+ left_arrow = nullptr;
+ }
+ if (right_arrow)
+ {
+ delete right_arrow;
+ right_arrow = nullptr;
+ }
+
+ // 删除底部"编辑"按钮
+ if (edit_button)
+ {
+ delete edit_button;
+ edit_button = nullptr;
+ }
+
+ // 删除主容器
+ delete group;
+ group = nullptr;
+ }
+
+ g_pMainView_WomenHealthView = nullptr;
+ }
+
+ /**
+ * @brief 获取女性健康界面的单例对象
+ * @return 返回当前女性健康界面的指针
+ * 作用:其他地方可以通过这个函数获取界面对象,进行界面更新等操作
+ */
+ WomenHealthView *WomenHealthView::GetInstance()
+ {
+ return g_pMainView_WomenHealthView;
+ }
+
+ /**
+ * @brief 判断当前是否显示今天所在月份
+ * @return true=当前显示今天所在月份, false=显示其他月份
+ *
+ * 作用:供Presenter调用,判断应该执行"编辑"还是"回今天"功能
+ */
+ bool WomenHealthView::IsShowingCurrentMonth()
+ {
+ return (show_y == today_y && show_m == today_m);
+ }
+
+ // 中文星期标签数组
+ static char *week_Z_str[] = {"一", "二", "三", "四", "五", "六", "日"};
+ // 英文星期标签数组
+ static char *week_E_str[] = {"MON", "THE", "WED", "THU", "FRI", "SAT", "SUN"};
+
+ /**
+ * @brief 计算某月有多少天
+ * @param year 年份
+ * @param month 月份(1-12)
+ * @return 该月的天数
+ */
+ int WomenHealthView::CalculateMonthDays(int year, int month)
+ {
+ // 判断是否闰年
+ bool isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+
+ if (month == 2) {
+ return isLeapYear ? 29 : 28; // 2月
+ } else if (month == 1 || month == 3 || month == 5 || month == 7 ||
+ month == 8 || month == 10 || month == 12) {
+ return 31; // 大月
+ } else {
+ return 30; // 小月
+ }
+ }
+
+ /**
+ * @brief 使用系统函数计算星期几(更准确)
+ * @param year 年份
+ * @param month 月份(1-12)
+ * @param day 日期
+ * @return 星期几(0=星期日, 1=星期一, ..., 6=星期六)
+ *
+ * 使用系统的 mktime 和 localtime 函数来计算星期,避免手动实现蔡勒公式可能的错误
+ */
+ int WomenHealthView::CalculateWeekday(int year, int month, int day)
+ {
+ struct tm timeStruct = {0};
+ timeStruct.tm_year = year - 1900; // tm_year是从1900年开始
+ timeStruct.tm_mon = month - 1; // tm_mon是0-11
+ timeStruct.tm_mday = day;
+
+ // 使用mktime来规范化时间结构,并自动计算星期
+ time_t t = mktime(&timeStruct);
+ if (t == -1) {
+ // 如果转换失败,使用蔡勒公式作为备用
+ int y = year;
+ int m = month;
+ if (m == 1 || m == 2) {
+ m += 12;
+ y--;
+ }
+ int w = (day + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7;
+ return (w + 6) % 7;
+ }
+
+ // 获取规范化后的时间结构
+ struct tm *result = localtime(&t);
+
+ // tm_wday: 0=星期日, 1=星期一, ..., 6=星期六(正好符合我们的需求)
+ return result->tm_wday;
+ }
+
+ /**
+ * @brief 初始化31个颜色标记圆圈
+ *
+ * 作用:创建用于标记月经期、排卵期等状态的圆圈
+ * 注意:必须先添加到容器,这样才会显示在日期标签下方
+ */
+ void WomenHealthView::InitDayMarkers()
+ {
+ for (int i = 0; i < 31; i++) {
+ if (dayMarker[i] == nullptr) {
+ dayMarker[i] = new UIImageView();
+ if (dayMarker[i] != nullptr) {
+ dayMarker[i]->Resize(46, 46);
+ dayMarker[i]->SetStyle(STYLE_BACKGROUND_OPA, 0xff);
+ dayMarker[i]->SetStyle(STYLE_BORDER_RADIUS, 23);
+ group->Add(dayMarker[i]);
+ dayMarker[i]->SetVisible(false); // 默认隐藏
+ }
+ }
+ }
+ }
+
+ /**
+ * @brief 初始化顶部标题"生理周期"
+ */
+ void WomenHealthView::InitTitleLabel()
+ {
+ if (title_label == nullptr) {
+ title_label = new UILabel();
+ }
+
+ title_label->SetPosition(0, 20); // 顶部中央
+ title_label->Resize(454, 40); // 全屏宽度
+ title_label->SetText("生理周期");
+ title_label->SetStyle(STYLE_TEXT_FONT, 28);
+ title_label->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ title_label->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ title_label->SetRollSpeed(0);
+ title_label->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 28);
+ title_label->SetTextColor(Color::White());
+
+ group->Add(title_label);
+ }
+
+ /**
+ * @brief 初始化日期标签(显示"X月X日 星期X")
+ * @param year 年份
+ * @param month 月份
+ * @param day 日期(当前日期)
+ * @param weekday 星期几
+ */
+ void WomenHealthView::InitMonthLabel(int year, int month, int day, int weekday)
+ {
+ if (mon_label == nullptr) {
+ mon_label = new UILabel();
+ }
+
+ mon_label->SetPosition(100, 75); // 标题下方,中央位置(左右留出箭头空间)
+ mon_label->Resize(254, 35); // 宽度留给左右箭头
+ mon_label->SetViewId("label1");
+ mon_label->SetStyle(STYLE_TEXT_FONT, 24);
+
+ // 格式化日期文本:"9月5日 星期五"
+ static const char* weekday_str[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
+ char date_text[50] = {0};
+ int wd = CalculateWeekday(year, month, day); // 计算星期几
+ sprintf(date_text, "%d月%d日 %s", month, day, weekday_str[wd]);
+ mon_label->SetText(date_text);
+
+ mon_label->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ mon_label->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ mon_label->SetRollSpeed(0);
+ mon_label->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 24);
+ mon_label->SetTextColor(Color::White());
+
+ group->Add(mon_label);
+ }
+
+ /**
+ * @brief 初始化左右箭头按钮
+ */
+ void WomenHealthView::InitArrowButtons()
+ {
+ // 左箭头按钮
+ if (left_arrow == nullptr) {
+ left_arrow = new UILabelButton();
+ }
+ left_arrow->SetPosition(40, 50); // 日期左侧
+ left_arrow->Resize(80, 80);
+ left_arrow->SetText("<");
+ left_arrow->SetStyle(STYLE_TEXT_FONT, 30);
+ left_arrow->SetStyle(STYLE_LINE_HEIGHT, 35);
+ left_arrow->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+
+ left_arrow->SetViewId(PREV_MONTH);
+ // 设置按钮背景样式
+ left_arrow->SetStyle(STYLE_BACKGROUND_COLOR, OPA_OPAQUE);
+ left_arrow->SetStyle(STYLE_BACKGROUND_OPA, OPA_OPAQUE);
+ left_arrow->SetStyleForState(STYLE_BACKGROUND_COLOR, OPA_OPAQUE, UIButton::PRESSED);
+ left_arrow->SetStyleForState(STYLE_BACKGROUND_OPA, OPA_OPAQUE, UIButton::PRESSED);
+ left_arrow->SetStyle(STYLE_BORDER_RADIUS, 100);
+ UIView::OnClickListener *clickListener = (UIView::OnClickListener *)OHOS::WomenHealthPresenter::GetInstance();
+ left_arrow->SetOnClickListener(clickListener);
+
+ group->Add(left_arrow);
+
+ // 右箭头按钮
+ if (right_arrow == nullptr) {
+ right_arrow = new UILabelButton();
+ }
+ right_arrow->SetPosition(320, 50); // 日期右侧
+ right_arrow->Resize(80, 80);
+ right_arrow->SetText(">");
+ right_arrow->SetStyle(STYLE_TEXT_FONT, 30);
+ right_arrow->SetStyle(STYLE_LINE_HEIGHT, 35);
+ right_arrow->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+
+ right_arrow->SetViewId(NEXT_MONTH);
+ // 设置按钮背景样式
+ right_arrow->SetStyle(STYLE_BACKGROUND_COLOR, OPA_OPAQUE);
+ right_arrow->SetStyle(STYLE_BACKGROUND_OPA, OPA_OPAQUE);
+ right_arrow->SetStyleForState(STYLE_BACKGROUND_COLOR, OPA_OPAQUE, UIButton::PRESSED);
+ right_arrow->SetStyleForState(STYLE_BACKGROUND_OPA, OPA_OPAQUE, UIButton::PRESSED);
+ right_arrow->SetStyle(STYLE_BORDER_RADIUS, 100);
+ UIView::OnClickListener *clickListener2 = (UIView::OnClickListener *)OHOS::WomenHealthPresenter::GetInstance();
+ right_arrow->SetOnClickListener(clickListener2);
+
+ group->Add(right_arrow);
+ }
+
+ /**
+ * @brief 初始化底部"编辑"按钮
+ */
+ void WomenHealthView::InitEditButton()
+ {
+ if (edit_button == nullptr) {
+ edit_button = new UILabelButton();
+ }
+
+ edit_button->SetPosition(118, 350); // 底部中央
+ edit_button->Resize(230, 80);
+ edit_button->SetText("编辑");
+ edit_button->SetStyle(STYLE_TEXT_FONT, 30);
+ edit_button->SetStyle(STYLE_LINE_HEIGHT, 35);
+ edit_button->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+ edit_button->SetViewId(EDIT);
+ // 设置按钮背景样式
+ edit_button->SetStyle(STYLE_BACKGROUND_COLOR, OPA_OPAQUE);
+ edit_button->SetStyle(STYLE_BACKGROUND_OPA, OPA_OPAQUE);
+ edit_button->SetStyleForState(STYLE_BACKGROUND_COLOR, OPA_OPAQUE, UIButton::PRESSED);
+ edit_button->SetStyleForState(STYLE_BACKGROUND_OPA, OPA_OPAQUE, UIButton::PRESSED);
+ edit_button->SetStyle(STYLE_BORDER_RADIUS, 100);
+ UIView::OnClickListener *clickListener = (UIView::OnClickListener *)OHOS::WomenHealthPresenter::GetInstance();
+ edit_button->SetOnClickListener(clickListener);
+
+ group->Add(edit_button);
+ }
+
+ /**
+ * @brief 初始化7个星期标签(一、二、三、四、五、六、日)
+ *
+ * 作用:显示日历的星期头部
+ * 注意:根据设计图要求,暂时不显示星期行
+ */
+ void WomenHealthView::InitWeekLabels()
+ {
+ for (int i = 0; i < 7; i++) {
+ if (week_label[i] == nullptr) {
+ week_label[i] = new UILabel();
+ }
+
+ // 设置位置:横向排列,起始X=58,每个间隔50像素
+ week_label[i]->SetPosition(58 + 50 * i, 110);
+ week_label[i]->Resize(50, 30);
+ week_label[i]->SetViewId("label3");
+ week_label[i]->SetStyle(STYLE_TEXT_FONT, 20);
+
+ // 根据系统语言设置中文或英文
+ if (ConfigModel::GetInstance()->GetLanguage() == ZH_CN) {
+ week_label[i]->SetText(week_Z_str[i]); // 中文:"一"、"二"等
+ } else {
+ week_label[i]->SetText(week_E_str[i]); // 英文:"MON"、"TUE"等
+ }
+
+ week_label[i]->SetLineBreakMode(UILabel::LINE_BREAK_WRAP);
+ week_label[i]->SetTextColor(Color::GetColorFromRGBA(0x66, 0x66, 0x66, 0xff)); // 灰色
+ week_label[i]->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ week_label[i]->SetRollSpeed(0);
+ week_label[i]->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 20);
+
+ group->Add(week_label[i]);
+ week_label[i]->SetVisible(false); // 暂时隐藏星期行(按设计图要求)
+ }
+ }
+
+ /**
+ * @brief 初始化31个日期标签UI对象(只创建,不设置内容)
+ *
+ * 作用:预先创建所有可能用到的日期标签对象
+ * 注意:因为去掉了星期行,日期起始Y坐标调整为120(原来是140)
+ */
+ void WomenHealthView::InitDayLabels()
+ {
+ for (int i = 0; i < 31; i++) {
+ if (day_label[i] == nullptr) {
+ day_label[i] = new UILabel();
+ }
+
+ day_label[i]->SetPosition(58, 120); // 初始位置(往上移了20像素)
+ day_label[i]->Resize(50, 44);
+ day_label[i]->SetViewId("label3");
+ day_label[i]->SetStyle(STYLE_TEXT_FONT, 20);
+ day_label[i]->SetStyle(STYLE_BORDER_RADIUS, 100);
+
+ day_label[i]->SetLineBreakMode(UILabel::LINE_BREAK_ELLIPSIS);
+ day_label[i]->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
+ day_label[i]->SetRollSpeed(0);
+ day_label[i]->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30);
+
+ group->Add(day_label[i]);
+ day_label[i]->SetVisible(false); // 默认隐藏
+ }
+ }
+
+ /**
+ * @brief 更新底部按钮文本
+ *
+ * 作用:根据当前显示的月份,动态修改按钮文本
+ * 规则:
+ * - 如果当前显示的是今天所在月份 → 显示"编辑"
+ * - 如果当前显示的不是今天所在月份 → 显示"回今天"
+ */
+ void WomenHealthView::UpdateEditButtonText()
+ {
+ if (edit_button == nullptr) {
+ return;
+ }
+
+ // 判断当前显示的是否是今天所在月份
+ if (show_y == today_y && show_m == today_m) {
+ edit_button->SetText("编辑");
+ } else {
+ edit_button->SetText("回今天");
+ }
+ }
+
+ /**
+ * @brief 回到今天所在的月份
+ *
+ * 作用:点击"回今天"按钮时调用,将界面切换回今天所在的月份
+ */
+ void WomenHealthView::BackToToday()
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "回到今天所在月份: %d年%d月", today_y, today_m);
+
+ // 第一步:隐藏所有日期标签和颜色标记
+ for (int i = 0; i < 31; i++) {
+ day_label[i]->SetVisible(false);
+ if (dayMarker[i]) {
+ dayMarker[i]->SetVisible(false);
+ }
+ }
+
+ // 第二步:恢复到今天的年月
+ show_y = today_y;
+ show_m = today_m;
+ show_d = today_d;
+
+ // 第三步:计算天数和星期
+ int mon_max_day = CalculateMonthDays(show_y, show_m);
+ int weekday = CalculateWeekday(show_y, show_m, 1);
+
+ // 第四步:更新日期标签
+ static const char* weekday_str[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
+ char date_text[50] = {0};
+ int wd = CalculateWeekday(show_y, show_m, show_d);
+ sprintf(date_text, "%d月%d日 %s", show_m, show_d, weekday_str[wd]);
+ mon_label->SetText(date_text);
+
+ // 第五步:更新日期显示
+ UpdateDayDisplay(show_y, show_m, mon_max_day, weekday);
+
+ // 第六步:更新箭头和按钮状态
+ UpdateArrowButtonsVisibility();
+ UpdateEditButtonText();
+
+ // 第七步:刷新界面
+ RootView::GetInstance()->Invalidate();
+ }
+
+ /**
+ * @brief 更新左右箭头按钮的显示状态
+ *
+ * 作用:根据当前显示的月份,判断是否显示左右箭头按钮
+ * 规则:
+ * 1. 如果当前月 = 经期开始月,隐藏左箭头(不能查看更早的月份)
+ * 2. 如果当前月 = 预测的最后一个月,隐藏右箭头(不能查看更晚的月份)
+ */
+ void WomenHealthView::UpdateArrowButtonsVisibility()
+ {
+ WomenHealthModel* model = WomenHealthModel::GetInstance();
+
+ // 如果还没有设置经期数据,显示所有箭头
+ if (!model->HasMenstrualData()) {
+ if (left_arrow) left_arrow->SetVisible(true);
+ if (right_arrow) right_arrow->SetVisible(true);
+ return;
+ }
+
+ // 获取经期开始的年月
+ int startYear = 0, startMonth = 0;
+ model->GetMenstrualStartYearMonth(startYear, startMonth);
+
+ // 获取预测的最后一个月
+ int lastYear = 0, lastMonth = 0;
+ model->GetLastPredictYearMonth(lastYear, lastMonth);
+
+ // 判断是否显示左箭头:当前月 == 经期开始月 时隐藏
+ if (left_arrow) {
+ if (show_y == startYear && show_m == startMonth) {
+ left_arrow->SetVisible(false); // 已经是最早的月份,隐藏左箭头
+ } else {
+ left_arrow->SetVisible(true);
+ }
+ }
+
+ // 判断是否显示右箭头:当前月 == 预测最后一个月 时隐藏
+ if (right_arrow) {
+ if (lastYear != 0 && show_y == lastYear && show_m == lastMonth) {
+ right_arrow->SetVisible(false); // 已经是最后的月份,隐藏右箭头
+ } else {
+ right_arrow->SetVisible(true);
+ }
+ }
+ }
+
+ /**
+ * @brief 更新日期显示(显示当月所有日期并绘制状态标记)
+ * @param year 年份
+ * @param month 月份
+ * @param maxDays 该月最大天数
+ * @param weekday 1号是星期几
+ */
+ void WomenHealthView::UpdateDayDisplay(int year, int month, int maxDays, int weekday)
+ {
+ // 显示当月的所有日期
+ for (int i = 0; i < maxDays; i++) {
+ // 计算该日期在日历表格中的位置
+ int x = (i + weekday) % 7; // 列位置(0-6)
+ int y = (i + weekday) / 7; // 行位置(0-5)
+
+ // 计算屏幕坐标(Y坐标从120开始,因为去掉了星期行)
+ int posX = 58 + 50 * x;
+ int posY = 120 + 44 * y;
+
+ // 设置日期标签位置和文本
+ day_label[i]->SetPosition(posX, posY);
+ char day[5];
+ sprintf(day, "%d", i + 1);
+ day_label[i]->SetText(day);
+
+ // 判断该日期的状态并绘制颜色标记
+ DayStatus status = GetDayStatus(year, month, i + 1);
+ DrawDayMarker(i, posX, posY, status);
+
+ day_label[i]->SetVisible(true);
+ }
+
+ // 隐藏多余的日期标签(如果从大月切换到小月)
+ for (int i = maxDays; i < 31; i++) {
+ day_label[i]->SetVisible(false);
+ dayMarker[i]->SetVisible(false);
+ }
+ }
+
+ /**
+ * @brief 判断某一天的状态
+ * @param year 年份
+ * @param month 月份
+ * @param day 日期
+ * @return DayStatus 返回该日期的状态
+ *
+ * 作用:根据女性健康数据模型,判断某天是月经期、排卵期、预测经期还是普通日
+ *
+ * 判断优先级(从高到低):
+ * 1. 今天(灰色)
+ * 2. 已记录月经期(红色)
+ * 3. 当前排卵期(蓝色)
+ * 4. 预测排卵期(蓝色) ← 新增
+ * 5. 预测月经期(粉色)
+ * 6. 普通日期(白色)
+ */
+ DayStatus WomenHealthView::GetDayStatus(uint32_t year, uint32_t month, uint32_t day)
+ {
+ // 获取当前系统时间
+ time_t now;
+ time(&now);
+ struct tm *nowInfo = localtime(&now);
+ uint32_t todayYear = nowInfo->tm_year + 1900;
+ uint32_t todayMonth = nowInfo->tm_mon + 1;
+ uint32_t todayDay = nowInfo->tm_mday;
+
+ // 如果是今天,返回今天状态(灰色圆圈)
+ if (year == todayYear && month == todayMonth && day == todayDay) {
+ return DAY_STATUS_TODAY;
+ }
+
+ // 从WomenHealthModel获取数据,判断该天所处的时期
+ WomenHealthModel* model = WomenHealthModel::GetInstance();
+
+ // 判断优先级:
+ // 1. 优先判断是否在已记录的月经期内(最高优先级)
+ if (model->IsInMenstrualPeriod(year, month, day)) {
+ return DAY_STATUS_MENSTRUAL; // 月经期(红色标记)
+ }
+
+ // 2. 判断是否在排卵期内(包括当前排卵期)
+ if (model->IsInOvulationPeriod(year, month, day)) {
+ return DAY_STATUS_OVULATION; // 排卵期(蓝色标记)
+ }
+
+ // 3. 判断是否在预测排卵期内(下次和下下次的排卵期,也是蓝色)
+ if (model->IsInPredictOvulationPeriod(year, month, day)) {
+ return DAY_STATUS_OVULATION; // 预测排卵期(蓝色标记)
+ }
+
+ // 4. 判断是否在预测经期内
+ if (model->IsInPredictPeriod(year, month, day)) {
+ return DAY_STATUS_PREDICT; // 预测经期(粉色标记)
+ }
+
+ // 5. 普通日期
+ return DAY_STATUS_NORMAL; // 普通日期(白色)
+ }
+
+ /**
+ * @brief 为指定日期绘制颜色标记圆圈
+ * @param dayIndex 日期索引(0-30,对应1-31号)
+ * @param x 圆圈的X坐标
+ * @param y 圆圈的Y坐标
+ * @param status 日期状态(决定圆圈颜色)
+ *
+ * 作用:根据日期状态绘制不同颜色的圆圈背景
+ * - 月经期:红色 (0xFFFF5A5A)
+ * - 排卵期:蓝色 (0xFF5AB0FF)
+ * - 预测经期:浅红色 (0xFFFFB5B5)
+ * - 今天:灰色 (0xFF808080)
+ * - 普通日:不显示圆圈
+ */
+ void WomenHealthView::DrawDayMarker(int dayIndex, int x, int y, DayStatus status)
+ {
+ // 如果是普通日期,隐藏标记
+ if (status == DAY_STATUS_NORMAL) {
+ if (dayMarker[dayIndex]) {
+ dayMarker[dayIndex]->SetVisible(false);
+ }
+ return;
+ }
+
+ // 如果标记对象不存在,创建它
+ if (dayMarker[dayIndex] == nullptr) {
+ dayMarker[dayIndex] = new UIImageView();
+ if (dayMarker[dayIndex] == nullptr) {
+ return;
+ }
+ dayMarker[dayIndex]->Resize(46,46); // 圆圈大小46*46
+ dayMarker[dayIndex]->SetStyle(STYLE_BACKGROUND_OPA, 0xff); // 不透明
+ dayMarker[dayIndex]->SetStyle(STYLE_BORDER_RADIUS, 23); // 圆角23,形成圆形
+ group->Add(dayMarker[dayIndex]);
+ }
+
+ // 根据状态设置颜色
+ uint32_t color = 0xFFFFFFFF; // 默认白色
+ switch (status) {
+ case DAY_STATUS_MENSTRUAL: // 月经期:红色
+ color = 0xFFFF5A5A;
+ break;
+ case DAY_STATUS_OVULATION: // 排卵期:蓝色
+ color = 0xFF5AB0FF;
+ break;
+ case DAY_STATUS_PREDICT: // 预测经期:浅红色
+ color = 0xFFFFB5B5;
+ break;
+ case DAY_STATUS_TODAY: // 今天:灰色
+ color = 0xFF808080;
+ break;
+ default:
+ break;
+ }
+
+ // 设置位置和颜色
+ dayMarker[dayIndex]->SetPosition(x, y);
+ dayMarker[dayIndex]->SetStyle(STYLE_BACKGROUND_COLOR, color);
+ dayMarker[dayIndex]->SetVisible(true);
+ }
+
+ /**
+ * @brief 更新日历标签显示(优化后的版本)
+ * @param type 切换类型:1=切换到上个月, 2=切换到下个月, 其他=刷新当前月
+ * @return 成功返回true,失败返回false
+ *
+ * 作用:当用户上下滑动或点击箭头时,切换月份并重新显示日历
+ *
+ * 工作流程:
+ * 1. 隐藏所有日期标签和标记
+ * 2. 获取边界月份(经期开始月和预测最后月)
+ * 3. 检查是否可以切换月份(防止超出边界)
+ * 4. 根据type参数计算新的年月
+ * 5. 更新日期标签"X月X日 星期X"
+ * 6. 调用UpdateDayDisplay更新显示
+ * 7. 更新箭头按钮显示状态
+ * 8. 刷新界面
+ */
+ bool WomenHealthView::updata_label(int8_t type)
+ {
+ // 第一步:隐藏所有日期标签和颜色标记
+ for (int i = 0; i < 31; i++) {
+ day_label[i]->SetVisible(false);
+ if (dayMarker[i]) {
+ dayMarker[i]->SetVisible(false);
+ }
+ }
+
+ // 第二步:获取Model数据,判断边界
+ WomenHealthModel* model = WomenHealthModel::GetInstance();
+
+ // 如果还没有设置经期数据,允许自由切换月份
+ if (model->HasMenstrualData()) {
+ // 获取经期开始的年月
+ int startYear = 0, startMonth = 0;
+ model->GetMenstrualStartYearMonth(startYear, startMonth);
+
+ // 获取预测的最后一个月
+ int lastYear = 0, lastMonth = 0;
+ model->GetLastPredictYearMonth(lastYear, lastMonth);
+
+ // 第三步:检查边界,防止超出范围
+ if (type == 1) { // 想要查看上个月
+ // 如果当前已经是经期开始的月份,不允许再往前
+ if (show_y == startYear && show_m == startMonth) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "已经是最早的月份,无法查看上个月");
+ return false;
+ }
+ } else if (type == 2) { // 想要查看下个月
+ // 如果当前已经是预测的最后一个月,不允许再往后
+ if (lastYear != 0 && show_y == lastYear && show_m == lastMonth) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "已经是最后的月份,无法查看下个月");
+ return false;
+ }
+ }
+ }
+
+ // 第四步:根据滑动方向更新年月
+ if (type == 1) { // 向上滑动或点击左箭头,显示上个月
+ show_m--;
+ if (show_m == 0) { // 跨年到去年12月
+ show_m = 12;
+ show_y--;
+ }
+ } else { // 向下滑动或点击右箭头,显示下个月
+ show_m++;
+ if (show_m == 13) { // 跨年到明年1月
+ show_m = 1;
+ show_y++;
+ }
+ }
+
+ // 第五步:使用辅助函数计算该月天数和1号星期几
+ int mon_max_day = CalculateMonthDays(show_y, show_m);
+ int weekday = CalculateWeekday(show_y, show_m, 1);
+
+ // 第六步:更新日期标签("X月X日 星期X"格式)
+ static const char* weekday_str[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
+ char date_text[50] = {0};
+ int wd = CalculateWeekday(show_y, show_m, show_d); // 使用当前保存的日期
+ sprintf(date_text, "%d月%d日 %s", show_m, show_d, weekday_str[wd]);
+ mon_label->SetText(date_text);
+
+ // 第七步:调用UpdateDayDisplay更新日期显示
+ UpdateDayDisplay(show_y, show_m, mon_max_day, weekday);
+
+ // 第八步:更新箭头按钮的显示状态
+ UpdateArrowButtonsVisibility();
+
+ // 第九步:更新底部按钮文本(编辑/回今天)
+ UpdateEditButtonText();
+
+ // 第十步:刷新屏幕显示
+ RootView::GetInstance()->Invalidate();
+ return true;
+ }
+ /**
+ * @brief 初始化所有标签和UI组件
+ * @return 成功返回true,失败返回false
+ *
+ * 作用:第一次进入界面时,创建所有需要的UI组件
+ *
+ * 工作流程:
+ * 1. 获取当前系统时间
+ * 2. 计算当月天数和1号星期几
+ * 3. 创建并设置月份标签
+ * 4. 创建并设置7个星期标签
+ * 5. 创建今日标记圆圈
+ * 6. 创建31个日期标签
+ * 7. 显示当月所有日期
+ * 8. 创建上拉动画
+ */
+ /**
+ * @brief 初始化所有UI组件(重构后的简洁版本)
+ *
+ * 作用:调用各个独立的初始化函数,降低耦合度
+ *
+ * 工作流程:
+ * 1. 获取当前系统时间
+ * 2. 计算当月天数和1号星期几
+ * 3. 依次调用各个初始化函数
+ * 4. 更新日期显示
+ */
+ bool WomenHealthView::Label_init()
+ {
+ // 第一步:获取当前系统时间
+ time_t currtime;
+ time(&currtime);
+ struct tm *timeinfo = localtime(&currtime);
+
+ int year = timeinfo->tm_year + 1900; // 年份
+ int month = timeinfo->tm_mon + 1; // 月份
+ int day = timeinfo->tm_mday; // 日期
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "女性健康界面初始化: %d年%d月%d日", year, month, day);
+
+ // 第二步:计算当月天数和1号星期几
+ int mon_max_day = CalculateMonthDays(year, month);
+ int weekday = CalculateWeekday(year, month, 1); // 计算1号是星期几
+
+ // 第三步:保存今天的日期(用于"回今天"功能)
+ today_y = year;
+ today_m = month;
+ today_d = day;
+
+ // 第四步:保存当前显示的年月日(用于后续切换月份)
+ show_y = year;
+ show_m = month;
+ show_d = day;
+
+ // 第五步:依次调用各个独立的初始化函数(降低耦合度)
+ InitDayMarkers(); // 初始化颜色标记圆圈
+ InitTitleLabel(); // 初始化顶部标题"生理周期"
+ InitMonthLabel(year, month, day, weekday); // 初始化日期标签"X月X日 星期X"
+ InitArrowButtons(); // 初始化左右箭头按钮
+ InitWeekLabels(); // 初始化星期标签(已隐藏)
+ InitDayLabels(); // 初始化日期标签
+ UpdateDayDisplay(year, month, mon_max_day, weekday); // 更新日期显示
+ InitEditButton(); // 初始化底部"编辑"按钮
+ UpdateArrowButtonsVisibility(); // 更新箭头按钮的显示状态
+ UpdateEditButtonText(); // 更新底部按钮文本(编辑/回今天)
+
+ return true;
+ }
+
+ /**
+ * @brief 界面启动函数 - 进入女性健康界面时调用
+ *
+ * 作用:创建主容器并初始化所有UI组件
+ *
+ * 工作流程:
+ * 1. 创建主容器(UIViewGroup)
+ * 2. 设置容器大小为全屏
+ * 3. 启用触摸和拖动功能(支持上下滑动切换月份)
+ * 4. 设置拖动监听器(由Presenter处理)
+ * 5. 调用Label_init()初始化所有标签
+ */
+ void WomenHealthView::OnStart()
+ {
+ // 创建主容器
+ group = new UIViewGroup();
+ if (group == nullptr)
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "WomenHealthView::创建主容器失败");
+ return;
+ }
+
+ // 设置容器属性
+ group->SetPosition(0, 0); // 位置:屏幕左上角
+ group->Resize(VERTICAL_RESOLUTION, HORIZONTAL_RESOLUTION); // 大小:全屏
+ group->SetTouchable(true); // 允许触摸
+ group->SetDraggable(true); // 允许拖动
+ group->SetOnDragListener(presenter_); // 设置拖动监听器,由Presenter处理滑动事件
+
+ // 将容器添加到根容器
+ AddViewToRootContainer(group);
+
+ //测试一下效果 设置今天为月经开始日期
+ TimeObj startDate = {2025, 10, 23};
+ WomenHealthModel::GetInstance()->SetMenstrualStartDate(startDate);
+
+ // 初始化所有标签和UI组件
+ if (!Label_init())
+ {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "WomenHealthView::初始化标签失败");
+ return;
+ }
+ }
+}
\ No newline at end of file