4. 按钮控件(lv_btn):按钮的创建与样式、点击事件处理、按钮状态(按下、释放、聚焦)、按钮矩阵(lv_btnmatrix)的使用。
按钮,说白了就是界面上那个你点一下就会触发动作的小方块。在 LVGL 里,lv_btn 是最基础的交互控件,但你别小看它——我见过不少项目,按钮的点击反馈没做好,用户总觉得界面「卡顿」或者「不跟手」。这一节,咱们就把按钮从里到外捋一遍。
4.1 按钮的创建与基础样式
创建一个按钮非常简单,几行代码就能跑起来。我个人习惯先创建一个屏幕对象,然后在上面添加按钮。
lv_obj_t *btn = lv_btn_create(lv_scr_act()); // 在当前屏幕创建按钮
lv_obj_set_size(btn, 120, 50); // 设置宽高
lv_obj_center(btn); // 居中显示
嗯,这里要注意:lv_btn_create 的父对象可以是任意容器,不一定非要是屏幕。如果你想让按钮放在一个面板里,把父对象传进去就行。
样式方面,LVGL 的按钮默认是扁平风格,但你可以通过 lv_style_t 来定制。我在项目中遇到过客户要求按钮要有「磨砂玻璃」效果,其实就是调了背景透明度加模糊,但 LVGL 本身不支持模糊,所以我用半透明背景加圆角模拟了那个质感。
核心样式属性:
- 背景颜色:
lv_style_set_bg_color() - 圆角半径:
lv_style_set_radius() - 边框宽度与颜色:
lv_style_set_border_width()/lv_style_set_border_color() - 阴影:
lv_style_set_shadow_width()/lv_style_set_shadow_color() - 文本颜色与字体:
lv_style_set_text_color()/lv_style_set_text_font()
举个例子,给按钮加个蓝色背景、白色文字、圆角加阴影:
static lv_style_t style_btn;
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn, lv_color_hex(0x2196F3));
lv_style_set_text_color(&style_btn, lv_color_white());
lv_style_set_radius(&style_btn, 8);
lv_style_set_shadow_width(&style_btn, 10);
lv_style_set_shadow_color(&style_btn, lv_color_hex(0x888888));
lv_obj_add_style(btn, &style_btn, 0); // 0 表示默认状态
4.2 点击事件处理
按钮不响应点击,那跟一张贴图有什么区别?LVGL 的事件系统非常灵活,你可以给按钮绑定一个事件回调。
void btn_event_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
LV_LOG_USER("按钮被点击了!");
// 这里写你的业务逻辑
}
}
lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL);
你想想看,除了 LV_EVENT_CLICKED,还有哪些常用事件?
| 事件代码 | 触发时机 | 我常用的场景 |
|---|---|---|
LV_EVENT_PRESSED |
手指按下瞬间 | 改变按钮颜色,给用户即时反馈 |
LV_EVENT_RELEASED |
手指抬起 | 恢复按钮颜色,或触发非点击类动作 |
LV_EVENT_CLICKED |
按下并抬起,且未滑动取消 | 主要业务逻辑触发点 |
LV_EVENT_LONG_PRESSED |
长按超过设定时间 | 快捷操作,比如删除确认 |
LV_EVENT_VALUE_CHANGED |
按钮状态变化(如切换开关) | 复选框、开关按钮用得多 |
小技巧: 如果你需要区分「点击」和「长按」,可以在同一个回调里判断事件码。我曾经在一个遥控器项目里,短按是切换频道,长按是进入设置,用户反馈说手感很自然。
4.3 按钮状态:按下、释放、聚焦
LVGL 的按钮有几种内置状态,每种状态都可以单独设置样式。说白了,就是让按钮在不同情况下「变脸」。
- LV_STATE_DEFAULT:默认状态,啥也没发生。
- LV_STATE_PRESSED:正在被按下。
- LV_STATE_FOCUSED:通过键盘或编码器聚焦到该按钮。
- LV_STATE_DISABLED:禁用状态,不可点击。
为什么要区分状态?我举个例子:你做一个智能家居面板,按钮按下时变暗,释放后恢复,聚焦时加个外发光——用户一看就知道当前操作的是哪个按钮。
static lv_style_t style_pr;
lv_style_init(&style_pr);
lv_style_set_bg_color(&style_pr, lv_color_hex(0x1565C0)); // 按下时深蓝
static lv_style_t style_focus;
lv_style_init(&style_focus);
lv_style_set_border_width(&style_focus, 3);
lv_style_set_border_color(&style_focus, lv_color_hex(0xFFEB3B)); // 聚焦时黄色边框
lv_obj_add_style(btn, &style_pr, LV_STATE_PRESSED);
lv_obj_add_style(btn, &style_focus, LV_STATE_FOCUSED);
注意: 状态样式的添加顺序很重要。LVGL 会按照状态的优先级来应用样式,如果你同时按下了又聚焦,LV_STATE_PRESSED 的样式会覆盖 LV_STATE_FOCUSED 的部分属性。我曾经在这里踩过坑——聚焦边框在按下时消失了,后来发现是样式优先级没搞清楚。
4.4 按钮矩阵 (lv_btnmatrix)
如果你需要一排按钮,比如数字键盘、菜单栏,一个一个创建 lv_btn 太麻烦了。这时候 lv_btnmatrix 就派上用场了。它本质上是一个按钮组,用一个控件管理多个按钮。
创建按钮矩阵的核心是定义一个字符串数组,每个字符串代表一个按钮的文本,"\n" 表示换行。
static const char *btnm_map[] = {
"1", "2", "3", "\n",
"4", "5", "6", "\n",
"7", "8", "9", "\n",
"0", "确认", ""
};
lv_obj_t *btnm = lv_btnmatrix_create(lv_scr_act());
lv_btnmatrix_set_map(btnm, btnm_map);
lv_obj_set_size(btnm, 300, 200);
lv_obj_center(btnm);
你想想看,这个矩阵里的每个按钮其实都有独立的索引。点击事件通过 LV_EVENT_VALUE_CHANGED 来捕获,然后通过 lv_btnmatrix_get_selected_btn() 获取当前点击的是哪个按钮。
void btnm_event_cb(lv_event_t *e) {
lv_obj_t *obj = lv_event_get_target(e);
uint32_t id = lv_btnmatrix_get_selected_btn(obj);
const char *txt = lv_btnmatrix_get_btn_text(obj, id);
LV_LOG_USER("按下了按钮 %d: %s", (int)id, txt);
}
lv_obj_add_event_cb(btnm, btnm_event_cb, LV_EVENT_VALUE_CHANGED, NULL);
按钮矩阵的常用配置:
- lv_btnmatrix_set_btn_width():设置某个按钮的相对宽度,比如让「确认」按钮占两列。
- lv_btnmatrix_set_one_checked():开启单选模式,类似收音机按钮组。
- lv_btnmatrix_set_ctrl_map():批量设置每个按钮的控制标志,比如禁用某个按钮。
我个人习惯在制作数字键盘时用按钮矩阵,因为布局整齐,代码量少。但要注意一点:按钮矩阵的所有按钮共享同一套样式。如果你需要某个按钮长得不一样(比如「确认」按钮是绿色的),那就得用 lv_btnmatrix_has_btn_ctrl() 配合自定义绘制,或者干脆单独创建一个 lv_btn 放在旁边。
4.5 知识体系总览
下面这张图把按钮相关的核心知识点串起来了,你可以对照着回顾一下。
这张图从左到右展示了按钮的三个核心维度:怎么创建和美化、怎么响应操作、怎么管理不同状态。最下面补充了按钮矩阵这个「批量生产」的工具。你写代码的时候,脑子里可以过一遍这个结构,不容易漏掉东西。
我的经验: 刚开始学 LVGL 时,我总喜欢把所有样式写在一个全局样式里,结果按钮一多就乱套。后来我养成了习惯:每个按钮组(比如导航栏、弹窗、键盘)各自维护一套局部样式,互不干扰。代码清晰了,调试也快。