LVGL 日历组件

LVGL 日历组件 calendar

添加到默认组中

  • lvgl的 calendar 组件会默认添加到旋钮按键组中,需要手动将其删除,否则会聚焦到不可见的控件上。
lv_obj_set_style_pad_all(calendar, 8, LV_STATE_DEFAULT);
lv_group_remove_obj(calendar);

修改时间范围

  • 年时间范围需要手动修改源码,下面就是将年信息修改到2023-2099年
/**
 * @file lv_calendar_obj_dropdown.c
 *
 */

 /*********************
  *      INCLUDES
  *********************/
#include "lv_calendar_header_dropdown.h"
#if LV_USE_CALENDAR_HEADER_DROPDOWN

#include "lv_calendar.h"
#include "../../../widgets/lv_dropdown.h"
#include "../../layouts/flex/lv_flex.h"

  /*********************
   *      DEFINES
   *********************/

   /**********************
    *      TYPEDEFS
    **********************/

    /**********************
     *  STATIC PROTOTYPES
     **********************/
static void my_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj);
static void year_event_cb(lv_event_t* e);
static void month_event_cb(lv_event_t* e);
static void value_changed_event_cb(lv_event_t* e);

/**********************
 *  STATIC VARIABLES
 **********************/
const lv_obj_class_t lv_calendar_header_dropdown_class = {
    .base_class = &lv_obj_class,
    .width_def = LV_PCT(100),
    .height_def = LV_SIZE_CONTENT,
    .constructor_cb = my_constructor
};

static const char* month_list = "01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12";
static const char* year_list = {
    "2023\n2024\n2025\n2026\n2027\n2028\n2029\n"
    "2030\n2031\n2032\n2033\n2034\n2035\n2036\n2037\n2038\n2039\n"
    "2040\n2041\n2042\n2043\n2044\n2045\n2046\n2047\n2048\n2049\n"
    "2050\n2051\n2052\n2053\n2054\n2055\n2056\n2057\n2058\n2059\n"
    "2060\n2061\n2062\n2063\n2064\n2065\n2066\n2067\n2068\n2069\n"
    "2070\n2071\n2072\n2073\n2074\n2075\n2076\n2077\n2078\n2079\n"
    "2080\n2081\n2082\n2083\n2084\n2085\n2086\n2087\n2088\n2089\n"
    "2090\n2091\n2092\n2093\n2094\n2095\n2096\n2097\n2098\n2099"

};

/**********************
 *      MACROS
 **********************/

 /**********************
  *   GLOBAL FUNCTIONS
  **********************/

lv_obj_t* lv_calendar_header_dropdown_create(lv_obj_t* parent)
{
    lv_obj_t* obj = lv_obj_class_create_obj(&lv_calendar_header_dropdown_class, parent);
    lv_obj_class_init_obj(obj);

    return obj;
}

/**********************
 *  STATIC FUNCTIONS
 **********************/

static void my_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj)
{
    LV_TRACE_OBJ_CREATE("begin");

    LV_UNUSED(class_p);

    lv_obj_t* calendar = lv_obj_get_parent(obj);
    lv_obj_move_to_index(obj, 0);
    lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_ROW);

    lv_obj_t* year_dd = lv_dropdown_create(obj);
    lv_dropdown_set_options(year_dd, year_list);
    lv_obj_add_event_cb(year_dd, year_event_cb, LV_EVENT_VALUE_CHANGED, calendar);
    lv_obj_set_flex_grow(year_dd, 1);

    lv_obj_t* month_dd = lv_dropdown_create(obj);
    lv_dropdown_set_options(month_dd, month_list);
    lv_obj_add_event_cb(month_dd, month_event_cb, LV_EVENT_VALUE_CHANGED, calendar);
    lv_obj_set_flex_grow(month_dd, 1);

    lv_obj_add_event_cb(obj, value_changed_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
    /*Refresh the drop downs*/
    lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
}

static void month_event_cb(lv_event_t* e)
{
    lv_obj_t* dropdown = lv_event_get_target(e);
    lv_obj_t* calendar = lv_event_get_user_data(e);

    uint16_t sel = lv_dropdown_get_selected(dropdown);

    const lv_calendar_date_t* d;
    d = lv_calendar_get_showed_date(calendar);
    lv_calendar_date_t newd = *d;
    newd.month = sel + 1;
    newd.day = 1;
    lv_calendar_set_showed_date(calendar, newd.year, newd.month);
    lv_calendar_set_today_date(calendar, newd.year, newd.month, newd.day);
    /*Refresh the drop downs*/
    lv_event_send(calendar, LV_EVENT_VALUE_CHANGED, NULL);
}

static void year_event_cb(lv_event_t* e)
{
    lv_obj_t* dropdown = lv_event_get_target(e);
    lv_obj_t* calendar = lv_event_get_user_data(e);

    uint16_t sel = lv_dropdown_get_selected(dropdown);

    const lv_calendar_date_t* d;
    d = lv_calendar_get_showed_date(calendar);
    lv_calendar_date_t newd = *d;
    newd.year = 2023 + sel;
    newd.day = 1;
    lv_calendar_set_showed_date(calendar, newd.year, newd.month);
    lv_calendar_set_today_date(calendar, newd.year, newd.month, newd.day);
    /*Refresh the drop downs*/
    lv_event_send(calendar, LV_EVENT_VALUE_CHANGED, NULL);
}

static void value_changed_event_cb(lv_event_t* e)
{
    lv_obj_t* header = lv_event_get_target(e);
    lv_obj_t* calendar = lv_obj_get_parent(header);
    const lv_calendar_date_t* cur_date = lv_calendar_get_showed_date(calendar);

    lv_obj_t* year_dd = lv_obj_get_child(header, 0);
    lv_dropdown_set_selected(year_dd, cur_date->year - 2023);

    lv_obj_t* month_dd = lv_obj_get_child(header, 1);
    lv_dropdown_set_selected(month_dd, cur_date->month - 1);
}

#endif /*LV_USE_CALENDAR_HEADER_ARROW*/


使用按键旋钮调整日期时,从设定日期开始调整请添加图片描述

需要修改lvgl源码,当 calendar 组建的 btnm 被聚焦时,修改被选择的按钮

static void focus_cb(lv_event_t* e)
{
    lv_calendar_t* calendar = (lv_calendar_t*)lv_event_get_user_data(e);
    uint8_t day_first = get_day_of_week(calendar->showed_date.year, calendar->showed_date.month, 1);
    lv_btnmatrix_set_selected_btn(calendar->btnm, calendar->today.day - 1 + day_first + 7);
}

lv_obj_add_event_cb(calendar->btnm, focus_cb, LV_EVENT_FOCUSED, calendar);

完整 lv_calendar.c 如下

/**
 * @file lv_calendar.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_calendar.h"
#include "../../../lvgl.h"
#if LV_USE_CALENDAR

#include "../../../misc/lv_assert.h"

/*********************
 *      DEFINES
 *********************/
#define LV_CALENDAR_CTRL_TODAY      LV_BTNMATRIX_CTRL_CUSTOM_1
#define LV_CALENDAR_CTRL_HIGHLIGHT  LV_BTNMATRIX_CTRL_CUSTOM_2

#define MY_CLASS &lv_calendar_class

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
static void draw_part_begin_event_cb(lv_event_t * e);

static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day);
static uint8_t get_month_length(int32_t year, int32_t month);
static uint8_t is_leap_year(uint32_t year);
static void highlight_update(lv_obj_t * calendar);

/**********************
 *  STATIC VARIABLES
 **********************/
const lv_obj_class_t lv_calendar_class = {
    .constructor_cb = lv_calendar_constructor,
    .width_def = (LV_DPI_DEF * 3) / 2,
    .height_def = (LV_DPI_DEF * 3) / 2,
    .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
    .instance_size = sizeof(lv_calendar_t),
    .base_class = &lv_obj_class
};

static const char * day_names_def[7] = LV_CALENDAR_DEFAULT_DAY_NAMES;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

lv_obj_t * lv_calendar_create(lv_obj_t * parent)
{
    LV_LOG_INFO("begin");
    lv_obj_t * obj = lv_obj_class_create_obj(&lv_calendar_class, parent);
    lv_obj_class_init_obj(obj);
    return obj;
}

/*=====================
 * Setter functions
 *====================*/

void lv_calendar_set_day_names(lv_obj_t * obj, const char * day_names[])
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_calendar_t * calendar = (lv_calendar_t *)obj;

    uint32_t i;
    for(i = 0; i < 7; i++) {
        calendar->map[i] = day_names[i];
    }
    lv_obj_invalidate(obj);
}

void lv_calendar_set_today_date(lv_obj_t * obj, uint32_t year, uint32_t month, uint32_t day)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_calendar_t * calendar = (lv_calendar_t *)obj;

    calendar->today.year         = year;
    calendar->today.month        = month;
    calendar->today.day          = day;

    highlight_update(obj);
}

void lv_calendar_set_highlighted_dates(lv_obj_t * obj, lv_calendar_date_t highlighted[], uint16_t date_num)
{
    LV_ASSERT_NULL(highlighted);

    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_calendar_t * calendar = (lv_calendar_t *)obj;

    calendar->highlighted_dates     = highlighted;
    calendar->highlighted_dates_num = date_num;

    highlight_update(obj);
}

void lv_calendar_set_showed_date(lv_obj_t * obj, uint32_t year, uint32_t month)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_calendar_t * calendar = (lv_calendar_t *)obj;

    calendar->showed_date.year   = year;
    calendar->showed_date.month  = month;
    calendar->showed_date.day    = 1;

    lv_calendar_date_t d;
    d.year = calendar->showed_date.year;
    d.month = calendar->showed_date.month;
    d.day = calendar->showed_date.day;

    uint32_t i;

    /*Remove the disabled state but revert it for day names*/
    lv_btnmatrix_clear_btn_ctrl_all(calendar->btnm, LV_BTNMATRIX_CTRL_DISABLED);
    for(i = 0; i < 7; i++) {
        lv_btnmatrix_set_btn_ctrl(calendar->btnm, i, LV_BTNMATRIX_CTRL_DISABLED);
    }

    uint8_t act_mo_len = get_month_length(d.year, d.month);
    uint8_t day_first = get_day_of_week(d.year, d.month, 1);
    uint8_t c;
    for(i = day_first, c = 1; i < act_mo_len + day_first; i++, c++) {
        lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
    }

    uint8_t prev_mo_len = get_month_length(d.year, d.month - 1);
    for(i = 0, c = prev_mo_len - day_first + 1; i < day_first; i++, c++) {
        lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
        lv_btnmatrix_set_btn_ctrl(calendar->btnm, i + 7, LV_BTNMATRIX_CTRL_DISABLED);
    }

    for(i = day_first + act_mo_len, c = 1; i < 6 * 7; i++, c++) {
        lv_snprintf(calendar->nums[i], sizeof(calendar->nums[0]), "%d", c);
        lv_btnmatrix_set_btn_ctrl(calendar->btnm, i + 7, LV_BTNMATRIX_CTRL_DISABLED);
    }

    highlight_update(obj);

    /*Reset the focused button if the days changes*/
    if(lv_btnmatrix_get_selected_btn(calendar->btnm) != LV_BTNMATRIX_BTN_NONE) {
        lv_btnmatrix_set_selected_btn(calendar->btnm, day_first + 7);
    }

    lv_obj_invalidate(obj);

    /* The children of the calendar are probably headers.
     * Notify them to let the headers updated to the new date*/
    uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    for(i = 0; i < child_cnt; i++) {
        lv_obj_t * child = lv_obj_get_child(obj, i);
        if(child == calendar->btnm) continue;
        lv_event_send(child, LV_EVENT_VALUE_CHANGED, obj);
    }
}

/*=====================
 * Getter functions
 *====================*/

lv_obj_t * lv_calendar_get_btnmatrix(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_calendar_t * calendar = (lv_calendar_t *)obj;
    return calendar->btnm;
}

const lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_calendar_t * calendar = (lv_calendar_t *)obj;

    return &calendar->today;
}

const lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    const lv_calendar_t * calendar = (lv_calendar_t *)obj;

    return &calendar->showed_date;
}

lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_calendar_t * calendar = (lv_calendar_t *)obj;

    return calendar->highlighted_dates;
}

uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * obj)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_calendar_t * calendar = (lv_calendar_t *)obj;

    return calendar->highlighted_dates_num;
}

lv_res_t lv_calendar_get_pressed_date(const lv_obj_t * obj, lv_calendar_date_t * date)
{
    LV_ASSERT_OBJ(obj, MY_CLASS);
    lv_calendar_t * calendar = (lv_calendar_t *)obj;

    uint16_t d = lv_btnmatrix_get_selected_btn(calendar->btnm);
    if(d == LV_BTNMATRIX_BTN_NONE) {
        date->year = 0;
        date->month = 0;
        date->day = 0;
        return LV_RES_INV;
    }

    const char * txt = lv_btnmatrix_get_btn_text(calendar->btnm, lv_btnmatrix_get_selected_btn(calendar->btnm));

    if(txt[1] == 0) date->day = txt[0] - '0';
    else date->day = (txt[0] - '0') * 10 + (txt[1] - '0');

    date->year = calendar->showed_date.year;
    date->month = calendar->showed_date.month;

    return LV_RES_OK;
}


/**********************
 *  STATIC FUNCTIONS
 **********************/
static void focus_cb(lv_event_t* e)
{
    lv_calendar_t* calendar = (lv_calendar_t*)lv_event_get_user_data(e);
    uint8_t day_first = get_day_of_week(calendar->showed_date.year, calendar->showed_date.month, 1);
    lv_btnmatrix_set_selected_btn(calendar->btnm, calendar->today.day - 1 + day_first + 7);
}
static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
{
    LV_UNUSED(class_p);
    lv_calendar_t * calendar = (lv_calendar_t *)obj;

    /*Initialize the allocated 'ext'*/
    calendar->today.year  = 2020;
    calendar->today.month = 1;
    calendar->today.day   = 1;

    calendar->showed_date.year  = 2020;
    calendar->showed_date.month = 1;
    calendar->showed_date.day   = 1;

    calendar->highlighted_dates      = NULL;
    calendar->highlighted_dates_num  = 0;

    lv_memset_00(calendar->nums, sizeof(calendar->nums));
    uint8_t i;
    uint8_t j = 0;
    for(i = 0; i < 8 * 7; i++) {
        /*Every 8th string is "\n"*/
        if(i != 0 && (i + 1) % 8 == 0) {
            calendar->map[i] = "\n";
        }
        else if(i < 7) {
            calendar->map[i] = day_names_def[i];
        }
        else {
            calendar->nums[j][0] = 'x';
            calendar->map[i] = calendar->nums[j];
            j++;
        }
    }
    calendar->map[8 * 7 - 1] = "";

    calendar->btnm = lv_btnmatrix_create(obj);
    lv_btnmatrix_set_map(calendar->btnm, calendar->map);
    lv_btnmatrix_set_btn_ctrl_all(calendar->btnm, LV_BTNMATRIX_CTRL_CLICK_TRIG | LV_BTNMATRIX_CTRL_NO_REPEAT);
    lv_obj_add_event_cb(calendar->btnm, draw_part_begin_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL);
    lv_obj_add_event_cb(calendar->btnm, focus_cb, LV_EVENT_FOCUSED, calendar);
    lv_obj_set_width(calendar->btnm, lv_pct(100));

    lv_obj_set_flex_flow(obj, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_grow(calendar->btnm, 1);

    lv_calendar_set_showed_date(obj, calendar->showed_date.year, calendar->showed_date.month);
    lv_calendar_set_today_date(obj, calendar->today.year, calendar->today.month, calendar->today.day);

    lv_obj_add_flag(calendar->btnm, LV_OBJ_FLAG_EVENT_BUBBLE);
}

static void draw_part_begin_event_cb(lv_event_t * e)
{
    lv_obj_t * obj = lv_event_get_target(e);
    lv_obj_draw_part_dsc_t * dsc = lv_event_get_param(e);
    if(dsc->part == LV_PART_ITEMS) {
        /*Day name styles*/
        if(dsc->id < 7) {
            dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
            dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
        }
        else if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED)) {
            dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
            dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
            dsc->label_dsc->color = lv_palette_main(LV_PALETTE_GREY);
        }

        if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_CALENDAR_CTRL_HIGHLIGHT)) {
            dsc->rect_dsc->bg_opa = LV_OPA_40;
            dsc->rect_dsc->bg_color = lv_theme_get_color_primary(obj);
            if(lv_btnmatrix_get_selected_btn(obj) == dsc->id) {
                dsc->rect_dsc->bg_opa = LV_OPA_70;
            }
        }

        if(lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_CALENDAR_CTRL_TODAY)) {
            dsc->rect_dsc->border_opa = LV_OPA_COVER;
            dsc->rect_dsc->border_color = lv_theme_get_color_primary(obj);
            dsc->rect_dsc->border_width += 1;
        }

    }
}

/**
 * Get the number of days in a month
 * @param year a year
 * @param month a month. The range is basically [1..12] but [-11..0] or [13..24] is also
 *              supported to handle next/prev. year
 * @return [28..31]
 */
static uint8_t get_month_length(int32_t year, int32_t month)
{
    month--;
    if(month < 0) {
        year--;             /*Already in the previous year (won't be less then -12 to skip a whole year)*/
        month = 12 + month; /*`month` is negative, the result will be < 12*/
    }
    if(month >= 12) {
        year++;
        month -= 12;
    }

    /*month == 1 is february*/
    return (month == 1) ? (28 + is_leap_year(year)) : 31 - month % 7 % 2;
}

/**
 * Tells whether a year is leap year or not
 * @param year a year
 * @return 0: not leap year; 1: leap year
 */
static uint8_t is_leap_year(uint32_t year)
{
    return (year % 4) || ((year % 100 == 0) && (year % 400)) ? 0 : 1;
}

/**
 * Get the day of the week
 * @param year a year
 * @param month a  month [1..12]
 * @param day a day [1..32]
 * @return [0..6] which means [Sun..Sat] or [Mon..Sun] depending on LV_CALENDAR_WEEK_STARTS_MONDAY
 */
static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day)
{
    uint32_t a = month < 3 ? 1 : 0;
    uint32_t b = year - a;

#if LV_CALENDAR_WEEK_STARTS_MONDAY
    uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + b + (b / 4) - (b / 100) + (b / 400) - 1) % 7;
#else
    uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + b + (b / 4) - (b / 100) + (b / 400)) % 7;
#endif

    return day_of_week  ;
}

static void highlight_update(lv_obj_t * obj)
{
    lv_calendar_t * calendar = (lv_calendar_t *)obj;
    uint16_t i;

    /*Clear all kind of selection*/
    lv_btnmatrix_clear_btn_ctrl_all(calendar->btnm, LV_CALENDAR_CTRL_TODAY | LV_CALENDAR_CTRL_HIGHLIGHT);

    uint8_t day_first = get_day_of_week(calendar->showed_date.year, calendar->showed_date.month, 1);
    if(calendar->highlighted_dates) {
        for(i = 0; i < calendar->highlighted_dates_num; i++) {
            if(calendar->highlighted_dates[i].year == calendar->showed_date.year &&
               calendar->highlighted_dates[i].month == calendar->showed_date.month) {
                lv_btnmatrix_set_btn_ctrl(calendar->btnm, calendar->highlighted_dates[i].day - 1 + day_first + 7,
                                          LV_CALENDAR_CTRL_HIGHLIGHT);
            }
        }
    }

    if(calendar->showed_date.year == calendar->today.year && calendar->showed_date.month == calendar->today.month) {
        lv_btnmatrix_set_btn_ctrl(calendar->btnm, calendar->today.day - 1 + day_first + 7, LV_CALENDAR_CTRL_TODAY);
    }
}

#endif  /*LV_USE_CALENDAR*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值