告别单调列表!用LVGL的Roller控件给你的嵌入式UI做个『日期选择器』(附完整代码)

张开发
2026/4/19 2:16:19 15 分钟阅读

分享文章

告别单调列表!用LVGL的Roller控件给你的嵌入式UI做个『日期选择器』(附完整代码)
嵌入式UI实战用LVGL Roller控件打造丝滑日期选择器在智能手表和智能家居面板的开发中日期选择器是个高频需求。传统的按钮加减方式操作繁琐而LVGL的Roller控件提供了更优雅的解决方案——通过手指滑动即可快速选择年月日。但如何将三个独立的Roller控件组合成一个联动的日期选择器这正是我们今天要解决的核心问题。1. 基础搭建创建年月日三联动Roller首先创建一个容器来承载三个Roller控件lv_obj_t *date_container lv_cont_create(lv_scr_act(), NULL); lv_obj_set_size(date_container, 300, 150); lv_obj_align(date_container, NULL, LV_ALIGN_CENTER, 0, 0);接着初始化年、月、日三个Roller// 年份Roller (2020-2030) lv_obj_t *year_roller lv_roller_create(date_container, NULL); lv_roller_set_options(year_roller, 2020\n2021\n2022\n2023\n2024\n2025\n2026\n2027\n2028\n2029\n2030, LV_ROLLER_MODE_NORMAL); // 月份Roller lv_obj_t *month_roller lv_roller_create(date_container, NULL); lv_roller_set_options(month_roller, 1月\n2月\n3月\n4月\n5月\n6月\n7月\n8月\n9月\n10月\n11月\n12月, LV_ROLLER_MODE_INFINITE); // 日期Roller (初始31天) lv_obj_t *day_roller lv_roller_create(date_container, NULL); char days[100]; for(int i1; i31; i) sprintf(days strlen(days), %d日\n, i); lv_roller_set_options(day_roller, days, LV_ROLLER_MODE_INFINITE);提示使用LV_ROLLER_MODE_INFINITE可以让月份和日期无限循环滚动提升用户体验2. 联动逻辑动态调整每月天数二月份的天数会根据闰年变化我们需要实现动态更新static void update_days(lv_obj_t *year_roller, lv_obj_t *month_roller, lv_obj_t *day_roller) { int year 2020 lv_roller_get_selected(year_roller); int month 1 lv_roller_get_selected(month_roller); int days_in_month; if(month 2) { // 闰年判断 int is_leap (year%40 year%100!0) || (year%4000); days_in_month is_leap ? 29 : 28; } else if(month4 || month6 || month9 || month11) { days_in_month 30; } else { days_in_month 31; } // 重建日期选项 char days_str[200] {0}; for(int d1; ddays_in_month; d) { sprintf(days_str strlen(days_str), %d日\n, d); } lv_roller_set_options(day_roller, days_str, LV_ROLLER_MODE_INFINITE); }为年份和月份Roller添加事件回调static void date_change_handler(lv_obj_t * obj, lv_event_t event) { if(event LV_EVENT_VALUE_CHANGED) { update_days(year_roller, month_roller, day_roller); } } lv_obj_set_event_cb(year_roller, date_change_handler); lv_obj_set_event_cb(month_roller, date_change_handler);3. 视觉优化打造专业级UI体验3.1 控件布局与对齐使用Flex布局让三个Roller等宽排列lv_cont_set_layout(date_container, LV_LAYOUT_PRETTY_MID); lv_obj_set_style_local_pad_inner(date_container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10);3.2 自定义选中区域样式static lv_style_t style_selected; lv_style_init(style_selected); lv_style_set_bg_color(style_selected, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x33, 0x8B, 0xFF)); lv_style_set_text_color(style_selected, LV_STATE_DEFAULT, LV_COLOR_WHITE); lv_obj_add_style(year_roller, LV_ROLLER_PART_SELECTED, style_selected); lv_obj_add_style(month_roller, LV_ROLLER_PART_SELECTED, style_selected); lv_obj_add_style(day_roller, LV_ROLLER_PART_SELECTED, style_selected);3.3 动画参数调优// 设置滚动动画时间为300ms lv_roller_set_anim_time(year_roller, 300); lv_roller_set_anim_time(month_roller, 300); lv_roller_set_anim_time(day_roller, 300); // 设置可见行数为5 lv_roller_set_visible_row_count(year_roller, 5); lv_roller_set_visible_row_count(month_roller, 5); lv_roller_set_visible_row_count(day_roller, 5);4. 高级功能扩展4.1 国际化支持通过宏定义实现多语言切换#if LANGUAGE CHINESE #define MONTH_OPTIONS 1月\n2月\n...12月 #define DAY_SUFFIX 日 #elif LANGUAGE ENGLISH #define MONTH_OPTIONS Jan\nFeb\n...Dec #define DAY_SUFFIX #endif4.2 日期范围限制添加最大最小日期限制bool is_date_valid(int year, int month, int day) { time_t t /* 转换为时间戳 */; return t MIN_DATE t MAX_DATE; }4.3 触摸优化// 增加点击区域 lv_obj_set_style_local_pad_all(year_roller, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, 15); lv_obj_set_style_local_pad_all(month_roller, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, 15); lv_obj_set_style_local_pad_all(day_roller, LV_ROLLER_PART_BG, LV_STATE_DEFAULT, 15);5. 性能优化技巧在资源受限的嵌入式设备上这些技巧能显著提升流畅度选项预生成提前生成好所有可能的日期选项避免运行时频繁拼接字符串样式复用多个Roller共用同一个样式对象减少内存占用事件节流对快速滑动事件进行去抖处理// 事件节流示例 static uint32_t last_event_time 0; static void throttled_handler(lv_obj_t * obj, lv_event_t event) { uint32_t now lv_tick_get(); if(now - last_event_time 100) { // 100ms内只处理一次 original_handler(obj, event); last_event_time now; } }6. 实际项目中的经验分享在智能家居面板项目中使用这个日期选择器时发现几个值得注意的细节当用户快速滑动时三个Roller的动画不同步会导致视觉上的割裂感。解决方案是使用lv_anim_timeline同步所有动画。在低端MCU上频繁更新日期选项会导致卡顿。最终采用预生成所有可能的日期字符串方案虽然占用更多Flash空间但换来流畅的操作体验。测试发现将可见行数设置为奇数如5行比偶数更符合视觉习惯因为中间选中行上下对称。

更多文章