资讯动态

LVGL进阶:改造更加节省内存的roller(滚轮)

发布时间:2026/8/3 20:47:16 来源:尧图企业网站定制
1.改造原因LVGL内置的roller控件在生成时申请比较多的内存特别是无限模式时需要7倍的文本选项内存。这是比较浪费的行为违背了lvgl小巧/节约的初衷。roller还有个麻烦之处如果想要数字序列选项还得写一个长长的字符串比如要一个表示年份的roller可能要写1900-2099的列表字符串太过于低效而无聊。这点完全可以使用更加便捷的方式替代。虽然spinbox可以替代数字选项功能但是用户使用上spinbox没有roller方便直观。2.思考原则上滚轮只需要申请当前需要显示的内容 以及即将显示的上下两项。查看源码LVGL的roller内部生成label控件通过移动label实现滚动。这个label占据了大量内存。要想节省内存完全可以不生成label,而是临时绘制各个选项当滑动时获取当前触点y坐标并记录下来根据滑动的距离实时绘制文本。考虑到有项数多的情况松开手后根据松手前的速度添加一个动画继续前进一段距离。3.代码自定义控件名为numroller,也支持传统方式创建。/** * Create the num roller * param parent pointer to a object parent * param min min value * param max max value * param digitbase 进制 比如:1010进制,特殊0/1汉字 * param alignnum 对齐数目比如3 则 12-012 */ lv_obj_t* lv_numroller_create(lv_obj_t* parent, int min ,int max, char digitbase, uint8_t alignnum); /** * Create the string roller 类似于roller的方式创建 * param parent pointer to a object parent * param strlist string ,以 \n为分割符 */ lv_obj_t* lv_numroller_create_str(lv_obj_t* parent, const char* strlist);定义控件类/********************* * DEFINES *********************/ #define PRIVATE #define NUMROLLER_digitbase_HAN_SER 0 //汉字数列 比如二〇〇四 #define NUMROLLER_digitbase_HAN_NUM 1 //汉字数字 只能处理10000的数 #define _NUMROLLER_digitbase_STR 0xff //使用字符列表项与内置roller相同的功能 #define NUMROLLER_digitAlign_NO 0 //不对齐 /********************** * TYPEDEFS **********************/ /*Data of NUMROLLER*/ typedef struct { PRIVATE lv_obj_t obj; int minvalue; //最小值 int maxvalue; //最大值 char* unit; //每一项的后缀,当使用字符型项时,用于记录strlist int16_t sel_id; //当前选项 PRIVATE uint16_t show_num_limit; PRIVATE lv_coord_t opt_h; PRIVATE lv_coord_t sel_opt_h; PRIVATE lv_coord_t ypos; PRIVATE lv_point_t prpt ; PRIVATE lv_coord_t old_py; PRIVATE lv_coord_t sel_lsp; uint16_t *strlistlens; //当使用字符型项时记录长度 minvalue0,maxvalue项目数量-1 PRIVATE int8_t anim_dir; uint8_t digitbase; //数据进制 ,特殊值: 0中文序列 ,1中文数字带十百千万 uint8_t digitAlignNum; //数字对齐 PRIVATE int8_t init; uint8_t move; //滚动指示用于判断sel_id的有效性 } lv_numroller_t;主要内容是默认回调函数。static void lv_numroller_event(const lv_obj_class_t* class_p, lv_event_t* e) { LV_UNUSED(class_p); lv_res_t res; /*Call the ancestors event handler*/ res lv_obj_event_base(MY_CLASS, e); if (res ! LV_RES_OK) return; lv_obj_t* obj lv_event_get_target(e); lv_numroller_t* roller (lv_numroller_t*)obj; if (e-code LV_EVENT_DRAW_MAIN) { lv_draw_ctx_t* draw_ctx lv_event_get_draw_ctx(e); const lv_area_t content_area; lv_obj_get_content_coords(obj, content_area); if (roller-opt_h 0 || roller-init 0) { roller-init 1; lv_coord_t h content_area.y2 - content_area.y1 1; lv_font_t* font lv_obj_get_style_text_font(obj, LV_PART_MAIN); roller-opt_h font-line_height lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); lv_font_t* selft lv_obj_get_style_text_font(obj, LV_PART_SELECTED); roller-sel_lsp lv_obj_get_style_text_line_space(obj, LV_PART_SELECTED); if (selft) { roller-sel_opt_h selft-line_height roller-sel_lsp; } else { roller-sel_opt_h roller-opt_h; } roller-show_num_limit (h (roller-opt_h - 1)) / roller-opt_h 2 ; roller-ypos get_ypos(roller, h, roller-sel_opt_h - roller-sel_lsp); } lv_coord_t midy content_area.y1 (content_area.y2 - content_area.y1 1) / 2; lv_area_t area; area.x1 content_area.x1; area.x2 content_area.x2; #if 1 { //sel opt area.y1 midy - roller-sel_opt_h / 2; area.y2 midy roller-sel_opt_h / 2; lv_draw_rect_dsc_t draw_dsc; lv_draw_rect_dsc_init(draw_dsc); lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN | LV_PART_SELECTED, draw_dsc); lv_draw_rect(draw_ctx, draw_dsc, area); //sel } #endif lv_draw_label_dsc_t label_dsc; lv_draw_label_dsc_init(label_dsc); lv_obj_init_draw_label_dsc(obj, LV_PART_MAIN, label_dsc); int offset; { lv_coord_t mpy roller-prpt.y - roller-old_py; if (mpy 0) { offset (mpy roller-opt_h / 2) / roller-opt_h; } else { offset (mpy - roller-opt_h / 2) / roller-opt_h; } } char* bufmid; char* bufstart get_opt_str_align(roller , roller-sel_id - ( roller-show_num_limit -1 ) / 2 - offset, roller-sel_id - offset, bufmid); if (bufstart 0)return; //y文字滑动 area.y1 content_area.y1 roller-ypos roller-prpt.y - roller-old_py - offset * roller-opt_h; area.y2 content_area.y2; #if 1 lv_area_t mask ; lv_area_t a1; { a1.x1 area.x1; a1.x2 area.x2; a1.y1 content_area.y1; a1.y2 midy - roller-sel_opt_h / 2; #if LV_DRAW_COMPLEX //遮罩因为是直接绘制而不是使用子控件所以需要对圆角外内容去除 int16_t mask_id; lv_draw_mask_radius_param_t* xmp; lv_coord_t rad lv_obj_get_style_radius(obj, LV_PART_MAIN); if (rad 1) { lv_draw_mask_radius_param_t mp; xmp mp; lv_draw_mask_radius_init(mp, obj-coords, rad, false); mask_id lv_draw_mask_add(mp, 0); } #endif //XXX 求区域交集并非多余不使用此法会导致重复绘制而字体加重 if (_lv_area_intersect(mask , draw_ctx-clip_area, a1)) { //修改绘制区域而不是文字区域这样比较方便定位 const lv_area_t* old draw_ctx-clip_area; draw_ctx-clip_area mask; lv_draw_label(draw_ctx, label_dsc, area, bufstart, NULL); draw_ctx-clip_area old; } a1.y1 midy roller-sel_opt_h / 2; a1.y2 content_area.y2; if (_lv_area_intersect(mask, draw_ctx-clip_area, a1)) { const lv_area_t* old draw_ctx-clip_area; draw_ctx-clip_area mask; if (roller-sel_opt_h roller-opt_h) { lv_draw_label(draw_ctx, label_dsc, area, bufstart, NULL); } else { lv_coord_t y area.y1; area.y1 (roller-show_num_limit-1 ) / 2 * (roller-opt_h) (roller-sel_opt_h- roller-opt_h) ; lv_draw_label(draw_ctx, label_dsc, area, bufmid, NULL); area.y1 y; } draw_ctx-clip_area old; } #if LV_DRAW_COMPLEX if (rad 1) { lv_draw_mask_free_param(xmp); lv_draw_mask_remove_id(mask_id); } #endif } lv_obj_init_draw_label_dsc(obj, LV_PART_SELECTED, label_dsc); a1.y1 midy - roller-sel_opt_h / 2 ; a1.y2 midy roller-sel_opt_h / 2; if (_lv_area_intersect(mask, draw_ctx-clip_area, a1)) { //sel opt txt const lv_area_t* clip_area_ori draw_ctx-clip_area; draw_ctx-clip_area mask; if (roller-sel_opt_h roller-opt_h) { lv_draw_label(draw_ctx, label_dsc, area, bufstart, NULL); } else { sel_id_to_str_align(roller , roller-sel_id - offset, bufstart); a1.y1 roller-sel_lsp / 2; lv_draw_label(draw_ctx, label_dsc, a1, bufstart, NULL); } draw_ctx-clip_area clip_area_ori; } #endif lv_mem_buf_release(bufstart); } else if (e-code LV_EVENT_PRESSED) { roller-move 1; lv_anim_del(obj, set_y_anim ); roller-anim_dir 0; lv_indev_get_point( lv_indev_get_act(), roller-prpt ); roller-old_py roller-prpt.y; } else if (e-code LV_EVENT_PRESSING) { lv_indev_t* indev lv_indev_get_act(); lv_point_t p; lv_indev_get_vect(indev, p); if (p.y) { lv_indev_get_point(indev, roller-prpt); lv_obj_invalidate(obj); } } else if (e-code LV_EVENT_RELEASED || e-code LV_EVENT_PRESS_LOST) { int cyc roller-maxvalue - roller-minvalue 1; lv_coord_t mpy roller-prpt.y - roller-old_py; if (mpy 0) { if (roller-sel_id cyc) roller-sel_id - cyc; else if (roller-sel_id 0) roller-sel_id cyc; roller-move 0; return; } if (mpy0) { roller-sel_id - (mpy roller-opt_h / 2) / roller-opt_h; } else { roller-sel_id - (mpy - roller-opt_h / 2) / roller-opt_h; } while (roller-sel_id 0) roller-sel_id cyc; while (roller-sel_id cyc) roller-sel_id - cyc; lv_point_t p; lv_indev_get_vect(lv_indev_get_act(), p); if (p.y 1)roller-anim_dir 1;else if (p.y -1 )roller-anim_dir -1; if (roller-anim_dir) { lv_coord_t time p.y0? p.y:-p.y; if (time 100 )time100; lv_anim_t a; lv_anim_init(a); lv_anim_set_var(a, obj); lv_anim_set_exec_cb(a, set_y_anim ); lv_anim_set_values(a, roller-sel_id, roller-sel_id p.y ); lv_anim_set_time(a, time*10 ); lv_anim_set_ready_cb(a, scroll_anim_ready_cb); //lv_anim_set_path_cb(a, lv_anim_path_ease_out); lv_anim_start(a); } else { roller-move 0; } roller-prpt.y 0; roller-old_py 0; lv_obj_invalidate(obj); } else if (e-code LV_EVENT_SIZE_CHANGED || e-codeLV_EVENT_STYLE_CHANGED) { roller-init 0; //lv_obj_invalidate(obj); } }4.效果经测试相同的UI,内存使用大约只相当于内置roller的1/3.

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价