#include "bike.h" static lv_obj_t* container = NULL; static lv_theme_t* theme_original; static const uint32_t bg_color = 0x040507; static int32_t battery_percent = 15; static lv_obj_t* battery_indicator = NULL; static lv_obj_t* battery_label = NULL; static void update_battery_indicator() { lv_label_set_text_fmt(battery_label, "%d%%", battery_percent); lv_obj_set_width(battery_indicator, lv_pct(max(battery_percent, 10))); if (battery_percent >= 60) { lv_obj_set_style_bg_color(battery_indicator, lv_color_hex(0x00ff00), 0); } else if (battery_percent >= 20) { lv_obj_set_style_bg_color(battery_indicator, lv_color_hex(0xffff00), 0); } else { lv_obj_set_style_bg_color(battery_indicator, lv_color_hex(0xff0000), 0); } } static void battery_timer_cb(lv_timer_t* timer) { battery_percent -= 1; if (battery_percent < 0) { battery_percent = 100; } update_battery_indicator(); } static void create_header() { //header lv_obj_t* header = lv_obj_create(container); lv_obj_set_size(header, lv_pct(100), 30); lv_obj_set_style_bg_color(header, lv_color_hex(bg_color), 0); lv_obj_set_style_border_side(header, LV_BORDER_SIDE_BOTTOM, 0); lv_obj_set_flex_align(header, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_layout(header, LV_LAYOUT_FLEX); lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW); lv_obj_set_style_pad_all(header, 4, 0); lv_obj_set_style_border_color(header, lv_color_hex(0x191B1D), 0); lv_obj_set_style_border_width(header, 1, 0); //time lv_obj_t* time_bar = lv_label_create(header); lv_obj_set_style_bg_color(time_bar, lv_color_hex(bg_color), 0); lv_label_set_text(time_bar, "12:35"); lv_obj_set_style_text_color(time_bar, lv_color_hex(0xffffff), 0); //gap lv_obj_t* gap_bar = lv_obj_create(header); lv_obj_set_size(gap_bar, LV_SIZE_CONTENT, lv_pct(100)); lv_obj_set_flex_grow(gap_bar, 1); lv_obj_set_style_bg_color(gap_bar, lv_color_hex(bg_color), 0); //battery //icon LV_IMG_DECLARE(battery); lv_obj_t* battery_container = lv_obj_create(header); lv_obj_set_style_bg_img_src(battery_container, &battery, 0); lv_obj_set_size(battery_container, 26, 16); lv_obj_set_style_margin_right(battery_container, 4, 0); lv_obj_set_style_pad_all(battery_container, 2, 0); lv_obj_set_style_pad_left(battery_container, 3, 0); lv_obj_set_style_pad_right(battery_container, 6, 0); battery_indicator = lv_obj_create(battery_container); lv_obj_set_size(battery_indicator, lv_pct(battery_percent), lv_pct(90)); lv_obj_set_style_radius(battery_indicator, 2, 0); lv_obj_set_align(battery_indicator, LV_ALIGN_LEFT_MID); battery_label = lv_label_create(header); lv_obj_set_style_bg_color(battery_label, lv_color_hex(bg_color), 0); lv_obj_set_size(battery_label, 40, LV_SIZE_CONTENT); lv_obj_set_style_text_color(battery_label, lv_color_hex(0xffffff), 0); update_battery_indicator(); lv_obj_t* timer = lv_timer_create(battery_timer_cb, 3 * 1000, NULL); } static void speed_arc_value_changed(lv_event_t* event) { lv_obj_t* arc = lv_event_get_target(event); int32_t value = lv_arc_get_value(arc); lv_obj_t* label = (lv_obj_t*)lv_event_get_user_data(event); lv_label_set_text_fmt(label, "%0.1f", value * 90.0 / 100.0); } static void create_speedmeter(lv_obj_t* parent) { lv_obj_update_layout(parent); lv_obj_t* arc = lv_arc_create(parent); lv_coord_t size = LV_MIN( lv_obj_get_width(parent), lv_obj_get_height(parent) ) * 0.8; lv_obj_set_size(arc, size, size); lv_obj_remove_style(arc, NULL, LV_PART_KNOB); lv_obj_set_style_arc_color(arc, lv_color_hex(0x367BF1), LV_PART_INDICATOR); lv_obj_set_style_arc_color(arc, lv_color_hex(0x21242A), LV_PART_MAIN); lv_obj_set_style_arc_width(arc, 12, LV_PART_INDICATOR); lv_obj_set_style_arc_width(arc, 12, LV_PART_MAIN); lv_obj_center(arc); lv_obj_update_layout(arc); //速度标签 lv_obj_t* speed_label = lv_label_create(parent); lv_obj_set_align(speed_label, LV_ALIGN_CENTER); lv_label_set_text(speed_label, "0.0"); LV_FONT_DECLARE(lv_font_din_28); lv_obj_set_style_text_font(speed_label, &lv_font_din_28, 0); lv_obj_set_style_text_color(speed_label, lv_color_hex(0xffffff), 0); lv_obj_t* speed_unit_label = lv_label_create(parent); lv_obj_align(speed_unit_label, LV_ALIGN_CENTER, 0, 20); lv_label_set_text(speed_unit_label, "km/h"); lv_obj_set_style_text_color(speed_unit_label, lv_color_hex(0xffffff), 0); lv_obj_add_event_cb(arc, speed_arc_value_changed, LV_EVENT_VALUE_CHANGED, speed_label); lv_arc_set_value(arc, 0); // 获取角度范围 int32_t start_angle = lv_arc_get_bg_angle_start(arc); int32_t end_angle = lv_arc_get_bg_angle_end(arc); int32_t angle_range = (end_angle >= start_angle) ? (end_angle - start_angle) : (360 - start_angle + end_angle); // 获取中心点 lv_coord_t cx = lv_obj_get_width(arc) / 2; lv_coord_t cy = lv_obj_get_height(arc) / 2; // 半径(用中心半径更准) lv_coord_t radius = LV_MIN(lv_obj_get_width(arc), lv_obj_get_height(arc)) / 2; // 画刻度 int max_speed = 90; static lv_point_precise_t line_points[(90 / 5 + 1) * 2] = { {0,0} }; for (int speed = 0; speed <= max_speed; speed += 5) { int32_t angle = start_angle + (angle_range * speed) / max_speed; if (angle >= 360) angle -= 360; int32_t cos_v = lv_trigo_cos(angle); int32_t sin_v = lv_trigo_sin(angle); lv_obj_t* line = lv_line_create(arc); lv_coord_t inner_r = radius; // 刻度内侧 lv_coord_t outer_r = radius; // 刻度外侧 // 粗细区分(10为大刻度) if (speed % 10 == 0) { lv_obj_set_style_line_width(line, 3, 0); inner_r -= 20; if (speed != 0 && speed != max_speed) { outer_r -= 13; } lv_obj_set_style_line_color(line, lv_color_hex(0xffffff), 0); } else { lv_obj_set_style_line_width(line, 2, 0); outer_r -= 14; inner_r -= 18; lv_obj_set_style_line_color(line, lv_color_hex(0x21242A), 0); } // 起点 lv_point_precise_t* p = line_points + (speed / 5) * 2; p[0].x = cx + (inner_r * cos_v) / 32767; p[0].y = cy + (inner_r * sin_v) / 32767; // 终点 p[1].x = cx + (outer_r * cos_v) / 32767; p[1].y = cy + (outer_r * sin_v) / 32767; if (speed % 10 == 0) { lv_coord_t text_r = inner_r - 15; lv_coord_t label_x = cx + (text_r * cos_v) / 32767; lv_coord_t label_y = cy + (text_r * sin_v) / 32767; lv_obj_t* label = lv_label_create(arc); lv_label_set_text_fmt(label, "%d", speed); lv_obj_update_layout(label); lv_obj_set_style_text_color(label, lv_color_hex(0xffffff), 0); lv_obj_set_pos(label, label_x - lv_obj_get_width(label) / 2, label_y - lv_obj_get_height(label) / 2); } lv_line_set_points(line, p, 2); } } void bike_init(lv_obj_t* parent) { //assert(container == NULL); lv_display_t* display = lv_display_get_default(); theme_original = lv_display_get_theme(display); lv_theme_t* theme = lv_theme_simple_init(display); lv_display_set_theme(display, theme); container = lv_obj_create(parent); lv_obj_set_size(container, lv_pct(100), lv_pct(100)); lv_obj_align(container, LV_ALIGN_CENTER, 0, 0); lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN); lv_obj_set_layout(container, LV_LAYOUT_FLEX); lv_obj_set_style_bg_color(container, lv_color_hex(bg_color), 0); create_header(); //内容 lv_obj_t* content = lv_obj_create(container); lv_obj_set_size(content, lv_pct(100), LV_SIZE_CONTENT); lv_obj_set_layout(content, LV_LAYOUT_FLEX); lv_obj_set_flex_flow(content, LV_FLEX_FLOW_ROW); lv_obj_set_style_bg_color(content, lv_color_hex(bg_color), 0); lv_obj_set_style_margin_top(content, 5, 0); lv_obj_set_flex_grow(content, 1); //码表 lv_obj_t* speedmeter_container = lv_obj_create(content); lv_obj_set_size(speedmeter_container, lv_pct(60), lv_pct(100)); lv_obj_set_style_bg_color(speedmeter_container, lv_color_hex(bg_color), 0); create_speedmeter(speedmeter_container); //底部 lv_obj_t* footer = lv_obj_create(container); lv_obj_set_size(footer, lv_pct(100), 30); lv_obj_set_style_bg_color(footer, lv_color_hex(bg_color), 0); lv_obj_set_style_margin_top(footer, 5, 0); lv_obj_set_style_border_side(footer, LV_BORDER_SIDE_FULL, 0); lv_obj_set_style_border_color(footer, lv_color_hex(0x191B1D), 0); lv_obj_set_style_border_width(footer, 1, 0); } void bike_deinit() { if (container) { lv_obj_delete(container); container = NULL; } }