LVGL仪表盘逆时针
背景
LVGL 8.3 自带的lv_meter控件,目前只支持顺时针显示,但是,项目上刚好用到了逆时针。官网论坛有讨论通过arc + lv_meter 组合的方式实现。这个方法在模拟器上可以,但是放到板子上,刻度显示不出来,只显示一个完整的弧,这不是我要的效果,具体为何如此,深入研究了一番,没找到原因。唯一的差别在模拟器使用的SDL,板子使用的FB。
思路
鉴于arc + lv_meter 组合的方案没有通过,转而换个思路。由于arc是支持逆时针的,那就先研究arc的实现方式,然后在lv_meter代码中修改。按照这个思路,又研究了一番源码,找到了方法。
实现
lv_meter_indicator_t
typedef struct{lv_meter_scale_t *scale;lv_meter_indicator_type_t type;lv_opa_t opa;int32_t start_value;int32_t end_value;union{// ... ...struct{uint16_t width;const void *src;lv_color_t color;int16_t r_mod;uint16_t mode; // 0: clockwise; 1: counter clockwise} arc;// ... ...} type_data;} lv_meter_indicator_t;
增加mode字段,用于描述顺时针还是逆时针。
lv_meter_add_arc
lv_meter_indicator_t *lv_meter_add_arc(lv_obj_t *obj, lv_meter_scale_t *scale, uint16_t width, lv_color_t color,int16_t r_mod, uint16_t mode)
{LV_ASSERT_OBJ(obj, MY_CLASS);lv_meter_t *meter = (lv_meter_t *)obj;lv_meter_indicator_t *indic = _lv_ll_ins_head(&meter->indicator_ll);LV_ASSERT_MALLOC(indic);lv_memset_00(indic, sizeof(lv_meter_indicator_t));indic->scale = scale;indic->opa = LV_OPA_COVER;indic->type = LV_METER_INDICATOR_TYPE_ARC;indic->type_data.arc.width = width;indic->type_data.arc.color = color;indic->type_data.arc.r_mod = r_mod;indic->type_data.arc.mode = mode;lv_obj_invalidate(obj);return indic;
}
增加输入参数,用于配置mode。
draw_arcs
static void draw_arcs(lv_obj_t *obj, lv_draw_ctx_t *draw_ctx, const lv_area_t *scale_area)
{// ... ..._LV_LL_READ_BACK(&meter->indicator_ll, indic){lv_meter_scale_t *scale = indic->scale;int32_t start = scale->rotation;int32_t end = scale->rotation + scale->angle_range;int32_t start_angle;int32_t end_angle;if (indic->type_data.arc.mode == 0){start_angle = lv_map(indic->start_value, scale->min, scale->max, start, end);end_angle = lv_map(indic->end_value, scale->min, scale->max, start, end);}else{start_angle = lv_map(indic->start_value, scale->min, scale->max, end, start);end_angle = lv_map(indic->end_value, scale->min, scale->max, end, start);}arc_dsc.start_angle = start_angle;arc_dsc.end_angle = end_angle;part_draw_dsc.radius = r_out + indic->type_data.arc.r_mod;part_draw_dsc.sub_part_ptr = indic;part_draw_dsc.p1 = &scale_center;lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);if (indic->type_data.arc.mode == 0){lv_draw_arc(draw_ctx, &arc_dsc, &scale_center, part_draw_dsc.radius, start_angle, end_angle);}else{lv_draw_arc(draw_ctx, &arc_dsc, &scale_center, part_draw_dsc.radius, end_angle, start_angle);}lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);}// ... ...
}
根据mode值,进行不同设置。
inv_arc
static void inv_arc(lv_obj_t *obj, lv_meter_indicator_t *indic, int32_t old_value, int32_t new_value)
{bool rounded = lv_obj_get_style_arc_rounded(obj, LV_PART_ITEMS);lv_area_t scale_area;lv_obj_get_content_coords(obj, &scale_area);lv_coord_t r_out = lv_area_get_width(&scale_area) / 2;lv_point_t scale_center;scale_center.x = scale_area.x1 + r_out;scale_center.y = scale_area.y1 + r_out;r_out += indic->type_data.arc.r_mod;lv_meter_scale_t *scale = indic->scale;int32_t start = scale->rotation;int32_t end = scale->rotation + scale->angle_range;int32_t start_angle;int32_t end_angle;if (indic->type_data.arc.mode == 0){start_angle = lv_map(old_value, scale->min, scale->max, start, end);end_angle = lv_map(new_value, scale->min, scale->max, start, end);}else{start_angle = lv_map(old_value, scale->min, scale->max, end, start);end_angle = lv_map(new_value, scale->min, scale->max, end, start);}lv_area_t a;lv_draw_arc_get_area(scale_center.x, scale_center.y, r_out, LV_MIN(start_angle, end_angle), LV_MAX(start_angle, end_angle), indic->type_data.arc.width, rounded, &a);lv_obj_invalidate_area(obj, &a);
}
根据mode值,进行不同设置。