LVGL(轻量级 GUI)

一、为什么选 LVGL

二、获取与编译

git clone https://github.com/lvgl/lvgl.git
git clone https://github.com/lvgl/lv_demos.git
git clone https://github.com/lvgl/lv_drivers.git   # 驱动(fbdev、sdl)

cd lvgl
mkdir build && cd build
cmake .. -DLVGL_CHOSEN_DEMO_MUSIC=1
make

三、集成到项目

# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(myapp)
set(CMAKE_CXX_STANDARD 14)

add_subdirectory(lvgl)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE lvgl)
target_include_directories(myapp PRIVATE lvgl)

四、Hello LVGL

// main.cpp
#include "lvgl.h"

int main(void)
{
    lv_init();

    // 1. 驱动初始化(framebuffer)
    lv_display_t *disp = lv_linux_fbdev_create();
    lv_linux_fbdev_set_file(disp, "/dev/fb0");

    // 2. 鼠标 / 触摸
    lv_indev_t *indev = lv_evdev_create(LV_INDEV_TYPE_POINTER, "/dev/input/event0");

    // 3. UI
    lv_obj_t *label = lv_label_create(lv_screen_active());
    lv_label_set_text(label, "Hello LVGL!");
    lv_obj_center(label);

    // 4. 主循环
    while (1) {
        lv_timer_handler();
        usleep(5000);
    }

    return 0;
}

五、常用组件

// 按钮
lv_obj_t *btn = lv_button_create(lv_screen_active());
lv_obj_set_size(btn, 100, 50);
lv_obj_center(btn);
lv_obj_add_event_cb(btn, my_event_cb, LV_EVENT_CLICKED, NULL);

lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Click Me");
lv_obj_center(label);

// 滑块
lv_obj_t *slider = lv_slider_create(lv_screen_active());
lv_slider_set_value(slider, 50, LV_ANIM_ON);

// 标签
lv_obj_t *label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "Hello");

// 图像
LV_IMAGE_DECLARE(my_img);
lv_obj_t *img = lv_image_create(lv_screen_active());
lv_image_set_src(img, &my_img);

六、主题

lv_theme_t *th = lv_theme_default_init(lv_display_get_default(),
                                        lv_palette_main(LV_PALETTE_BLUE),
                                        lv_palette_main(LV_PALETTE_RED),
                                        true, LV_FONT_DEFAULT);
lv_display_set_theme(lv_display_get_default(), th);

七、字体

// 编译时
LV_FONT_DECLARE(my_font_32)
lv_obj_set_style_text_font(label, &my_font_32, 0);

// 或启用自带
LV_FONT_MONTSERRAT_32
LV_FONT_DEJAVU_16
LV_FONT_UNSCII_8

八、动画

lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, obj);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_time(&a, 1000);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_x);
lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);
lv_anim_start(&a);

九、移植到新平台

需要实现 3 个 tick:

uint32_t my_tick_get(void) {
    static uint64_t start_ms = 0;
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    uint64_t now_ms = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    if (start_ms == 0) start_ms = now_ms;
    return (uint32_t)(now_ms - start_ms);
}

// LVGL 8.x
lv_tick_set_cb(my_tick_get);

十、显示缓冲(避免撕裂)

static lv_draw_buf_t buf;
static lv_color_t buf1[DISP_WIDTH * 10 * sizeof(lv_color16_t)];
lv_display_set_buffers(disp, buf1, NULL, sizeof(buf1), LV_DISPLAY_RENDER_MODE_PARTIAL);

十一、触摸支持

static void my_input_read(lv_indev_t *indev, lv_indev_data_t *data) {
    // 读触摸
    if (touch_pressed()) {
        data->point.x = touch_x;
        data->point.y = touch_y;
        data->state = LV_INDEV_STATE_PRESSED;
    } else {
        data->state = LV_INDEV_STATE_RELEASED;
    }
}

lv_indev_t *indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, my_input_read);

十二、内存监控

lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
printf("used: %d%% (%d/%d)\n", mon.used_pct, mon.used_size, mon.total_size);

十三、LVGL 与 Linux

十四、嵌入式 LVGL 项目结构

// ui/
// ├── screens.h
// ├── screens.cpp
// ├── components/
// └── events.cpp

#include "screens.h"

void create_main_screen(void) {
    lv_obj_t *scr = lv_obj_create(NULL);
    // ...
    lv_screen_load(scr);
}

十五、推荐资源