4、LVGL基础控件(下):文本框、下拉列表、复选框、列表、表格
上一章我们把按钮、标签、滑块这些基础控件过了一遍。今天继续往下走,聊几个在表单和菜单场景里特别常用的控件。说实话,这些控件才是真正决定交互体验的关键——用户输入、选项选择、数据展示,全得靠它们。
我个人习惯把这一组控件叫做“表单五虎将”。它们各有各的脾气,用好了能让界面交互行云流水,用不好嘛……嗯,我在项目里踩过的坑,今天一并给你抖出来。
本章核心知识点一览:
- 文本框:输入、密码模式、光标控制
- 下拉列表:选项管理、选中事件
- 复选框:多选逻辑、状态回调
- 列表:动态增删、点击响应
- 表格:行列配置、单元格操作
4.1 文本框(lv_textarea)—— 用户输入的守门员
文本框是用户和系统对话的窗口。你想想看,WiFi密码输入、设备命名、搜索关键词……哪个离得开它?
创建文本框其实很简单:
lv_obj_t * ta = lv_textarea_create(lv_scr_act());
lv_obj_set_size(ta, 300, 80);
lv_obj_align(ta, LV_ALIGN_CENTER, 0, 0);
lv_textarea_set_placeholder_text(ta, "请输入设备名称...");
这里有个细节——占位文本。我刚开始做项目时总忽略它,结果用户面对一个空白框完全不知道要填什么。后来每个文本框我都加上 placeholder,用户体验提升了一大截。
实用技巧:
- 密码模式:
lv_textarea_set_password_mode(ta, true);输入内容自动变圆点 - 最大长度:
lv_textarea_set_max_length(ta, 20);防止用户乱输 - 文本过滤:
lv_textarea_set_accepted_chars(ta, "0123456789");只允许数字
我曾经在一个门禁项目里,用户输入密码时没限制长度,结果有人输了一长串字符导致缓冲区溢出……从那以后,set_max_length 成了我每个文本框的标配。
获取用户输入也很直接:
const char * text = lv_textarea_get_text(ta);
LV_LOG_USER("用户输入: %s", text);
注意:文本框默认会显示光标。如果你不需要,记得调用 lv_textarea_set_cursor_blink_time(ta, 0); 关闭闪烁。否则在某些场景下光标会一直闪,看着很别扭。
4.2 下拉列表(lv_dropdown)—— 节省空间的利器
下拉列表说白了就是一个“折叠起来的选项菜单”。平时只显示当前选中项,点击后才展开所有选项。这在屏幕寸土寸金的嵌入式设备上特别实用。
创建方式:
lv_obj_t * dd = lv_dropdown_create(lv_scr_act());
lv_obj_set_size(dd, 200, 40);
lv_obj_align(dd, LV_ALIGN_CENTER, 0, 0);
lv_dropdown_set_options(dd, "选项一\n选项二\n选项三\n选项四");
注意选项之间用 \n 分隔,这是 LVGL 的约定。我见过有人用逗号分隔,结果所有选项挤在一行里……
监听选中事件:
void dropdown_event_cb(lv_event_t * e) {
lv_obj_t * dd = lv_event_get_target(e);
char buf[64];
lv_dropdown_get_selected_str(dd, buf, sizeof(buf));
LV_LOG_USER("选中: %s", buf);
}
lv_obj_add_event_cb(dd, dropdown_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
我踩过的坑:下拉列表展开后,如果列表项太多,可能会超出屏幕边界。解决办法是设置固定高度:lv_dropdown_set_options_height(dd, 150); 限制展开区域高度,超出部分自动滚动。
4.3 复选框(lv_checkbox)—— 多选场景的标配
复选框这东西,看着简单,用起来其实有不少门道。它本质上是一个带文本标签的方框,点击切换选中状态。
lv_obj_t * cb = lv_checkbox_create(lv_scr_act());
lv_checkbox_set_text(cb, "记住密码");
lv_obj_align(cb, LV_ALIGN_CENTER, 0, 0);
获取状态:
bool checked = lv_obj_has_state(cb, LV_STATE_CHECKED);
if (checked) {
LV_LOG_USER("复选框已选中");
}
我个人习惯在复选框的事件回调里做状态处理,而不是轮询:
void checkbox_event_cb(lv_event_t * e) {
lv_obj_t * cb = lv_event_get_target(e);
if (lv_obj_has_state(cb, LV_STATE_CHECKED)) {
// 执行选中逻辑
} else {
// 执行取消选中逻辑
}
}
小技巧:如果你需要实现“全选/全不选”功能,可以用 lv_obj_add_state(cb, LV_STATE_CHECKED) 和 lv_obj_clear_state(cb, LV_STATE_CHECKED) 批量控制。
4.4 列表(lv_list)—— 菜单和选项的容器
列表是嵌入式UI里最常见的导航组件。设置菜单、功能列表、联系人列表……几乎无处不在。
创建列表并添加项:
lv_obj_t * list = lv_list_create(lv_scr_act());
lv_obj_set_size(list, 240, 300);
lv_obj_align(list, LV_ALIGN_CENTER, 0, 0);
lv_obj_t * btn1 = lv_list_add_btn(list, LV_SYMBOL_WIFI, "WiFi设置");
lv_obj_t * btn2 = lv_list_add_btn(list, LV_SYMBOL_BLUETOOTH, "蓝牙");
lv_obj_t * btn3 = lv_list_add_btn(list, LV_SYMBOL_SETTINGS, "系统设置");
每个列表项其实就是一个按钮,前面可以带图标。图标用 LVGL 内置的符号常量,比如 LV_SYMBOL_WIFI、LV_SYMBOL_HOME 等。
点击响应:
void list_event_cb(lv_event_t * e) {
lv_obj_t * btn = lv_event_get_target(e);
const char * text = lv_list_get_btn_text(list, btn);
LV_LOG_USER("点击: %s", text);
// 根据 text 跳转到对应页面
}
lv_obj_add_event_cb(list, list_event_cb, LV_EVENT_CLICKED, NULL);
注意:列表项的事件要绑定在列表对象上,而不是每个按钮上。LVGL 的事件冒泡机制会自动处理。我之前犯过傻,给每个按钮单独绑事件,结果代码又臭又长。
动态增删列表项也很常见:
// 添加
lv_obj_t * new_btn = lv_list_add_btn(list, LV_SYMBOL_EDIT, "新建项");
// 删除(需要获取按钮对象)
lv_obj_del(btn_to_delete);
4.5 表格(lv_table)—— 数据展示的硬骨头
表格是这几个控件里最“重”的一个。它用来展示结构化数据,比如参数列表、日志记录、设备状态等。
创建表格:
lv_obj_t * table = lv_table_create(lv_scr_act());
lv_obj_set_size(table, 400, 200);
lv_obj_align(table, LV_ALIGN_CENTER, 0, 0);
// 设置列数和行数
lv_table_set_col_cnt(table, 3);
lv_table_set_row_cnt(table, 5);
填充数据:
// 表头
lv_table_set_cell_value(table, 0, 0, "参数名");
lv_table_set_cell_value(table, 0, 1, "当前值");
lv_table_set_cell_value(table, 0, 2, "单位");
// 数据行
lv_table_set_cell_value(table, 1, 0, "温度");
lv_table_set_cell_value(table, 1, 1, "25.6");
lv_table_set_cell_value(table, 1, 2, "°C");
lv_table_set_cell_value(table, 2, 0, "湿度");
lv_table_set_cell_value(table, 2, 1, "68");
lv_table_set_cell_value(table, 2, 2, "%");
调整列宽:
lv_table_set_col_width(table, 0, 120);
lv_table_set_col_width(table, 1, 100);
lv_table_set_col_width(table, 2, 80);
实战经验:表格的单元格内容默认是居中对齐的。如果你需要左对齐,可以这样设置:lv_table_set_cell_align(table, row, col, LV_TEXT_ALIGN_LEFT);。我在做设备参数表时,数值右对齐、文本左对齐,看起来专业多了。
表格的单元格点击事件:
void table_event_cb(lv_event_t * e) {
lv_obj_t * table = lv_event_get_target(e);
lv_table_cell_t * cell = lv_table_get_selected_cell(table);
if (cell) {
LV_LOG_USER("选中单元格: 行%d 列%d", cell->row, cell->col);
}
}
lv_obj_add_event_cb(table, table_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
注意:表格的单元格数量不要太多。LVGL 的表格是纯文本渲染,如果行列数超过 50x50,刷新性能会明显下降。大数据量建议用列表或自定义控件。
4.6 综合示例:一个简单的设置页面
把上面几个控件组合起来,就是一个完整的设置页面:
// 创建页面容器
lv_obj_t * page = lv_obj_create(lv_scr_act());
lv_obj_set_size(page, 320, 480);
// 文本框 - 设备名称
lv_obj_t * ta = lv_textarea_create(page);
lv_obj_set_size(ta, 280, 40);
lv_obj_align(ta, LV_ALIGN_TOP_MID, 0, 20);
lv_textarea_set_placeholder_text(ta, "设备名称");
// 下拉列表 - 工作模式
lv_obj_t * dd = lv_dropdown_create(page);
lv_obj_set_size(dd, 280, 40);
lv_obj_align(dd, LV_ALIGN_TOP_MID, 0, 80);
lv_dropdown_set_options(dd, "自动模式\n手动模式\n休眠模式");
// 复选框 - 启用通知
lv_obj_t * cb = lv_checkbox_create(page);
lv_checkbox_set_text(cb, "启用通知");
lv_obj_align(cb, LV_ALIGN_TOP_MID, 0, 140);
// 列表 - 功能菜单
lv_obj_t * list = lv_list_create(page);
lv_obj_set_size(list, 280, 120);
lv_obj_align(list, LV_ALIGN_TOP_MID, 0, 200);
lv_list_add_btn(list, LV_SYMBOL_WIFI, "网络设置");
lv_list_add_btn(list, LV_SYMBOL_BLUETOOTH, "蓝牙配对");
lv_list_add_btn(list, LV_SYMBOL_SETTINGS, "高级设置");
这个例子虽然简单,但涵盖了本章所有控件的核心用法。你可以在实际项目中按这个模板扩展。
最后说一句:这些控件看着基础,但组合起来能做出很复杂的交互界面。关键是理解每个控件的特性,知道什么时候该用哪个。比如多选用 checkbox,单选用 dropdown,导航用 list,数据展示用 table。选对了工具,开发效率翻倍。