[LVGL] 从0开始,学LVGL:基础构建篇 - 掌握UI的核心构建块
第2部分:基础构建篇 - 掌握UI的核心构建块
文章目录
- **第2部分:基础构建篇 - 掌握UI的核心构建块**
- **第3章:LVGL的对象模型与核心概念**
- **3.1 万物皆对象:理解LVGL的面向对象设计**
- **3.2 对象树:理解父子关系**
- **3.3 屏幕:对象的根容器**
- **3.4 实战:创建你的第一个对象树**
- **第4章:让界面"活"起来 - 样式系统入门**
- **4.1 为什么需要样式系统?**
- **4.2 样式的数学模型**
- **4.3 理解样式状态**
- **4.4 创建和应用样式**
- **4.5 常用样式属性详解**
- **第5章:基础控件(一)- 标签、按钮与基础容器**
- **5.1 标签:文字的载体**
- **5.2 按钮:交互的基础**
- **5.3 基础容器:布局的基石**
- **第6章:基础控件(二)- 滑块、开关、进度条**
- **6.1 滑块:精确的数值输入**
- **6.2 开关:二进制状态选择**
- **6.3 进度条:操作反馈与状态显示**
- **6.4 综合实战:创建简易调光器界面**
- **本章总结与挑战**
第3章:LVGL的对象模型与核心概念
3.1 万物皆对象:理解LVGL的面向对象设计
在LVGL中,一切可视元素都是对象。按钮是对象,标签是对象,屏幕本身也是一个对象。这种统一的对象模型带来了极大的灵活性和一致性。
从面向对象的角度看,LVGL实现了一个继承体系:
所有控件都继承自基础对象 lv_obj_t
,这意味着:
- 所有对象都有共同的基本属性(位置、大小、父对象等)
- 所有对象都支持共同的操作(创建、删除、移动、设置样式等)
- 每个派生对象添加自己特有的功能(如标签的文本、滑块的数值)
3.2 对象树:理解父子关系
LVGL通过父子关系构建了一个对象树结构,这决定了:
- 渲染顺序:父对象先渲染,子对象后渲染
- 坐标系统:子对象的坐标相对于父对象
- 可见性:如果父对象不可见,所有子对象都不可见
- 生命周期:删除父对象会自动删除所有子对象
对象树示例:
3.3 屏幕:对象的根容器
屏幕是对象树的根节点。每个LVGL应用至少有一个屏幕:
/* 获取当前活动屏幕 */
lv_obj_t * screen = lv_scr_act();/* 创建新的屏幕 */
lv_obj_t * new_screen = lv_obj_create(NULL);/* 切换到新屏幕 */
lv_scr_load(new_screen);
3.4 实战:创建你的第一个对象树
让我们通过代码来理解这些概念:
#include <lvgl.h>
#include <lv_drivers/display/monitor.h>
#include <lv_drivers/indev/mouse.h>
#include <stdlib.h>// ... 省略初始化代码(与第一部分相同)int main(void) {// LVGL初始化(省略,参考第一部分)/********************* 创建对象树********************/// 获取当前活动屏幕lv_obj_t * screen = lv_scr_act();// 1. 创建一个容器作为父对象lv_obj_t * container = lv_obj_create(screen);lv_obj_set_size(container, 200, 150); // 设置大小lv_obj_set_pos(container, 50, 50); // 设置位置lv_obj_set_style_bg_color(container, lv_color_hex(0xE0E0E0), 0); // 浅灰色背景// 2. 在容器内创建标签(子对象)lv_obj_t * label1 = lv_label_create(container);lv_label_set_text(label1, "子标签 1");lv_obj_set_pos(label1, 10, 10); // 相对于容器的位置// 3. 在容器内创建另一个标签lv_obj_t * label2 = lv_label_create(container);lv_label_set_text(label2, "子标签 2");lv_obj_set_pos(label2, 10, 40);// 4. 直接在屏幕上创建按钮(屏幕的子对象)lv_obj_t * button = lv_btn_create(screen);lv_obj_set_size(button, 120, 50);lv_obj_set_pos(button, 300, 50);// 5. 在按钮内创建标签(按钮的子对象)lv_obj_t * btn_label = lv_label_create(button);lv_label_set_text(btn_label, "按钮");lv_obj_center(btn_label);// 主循环(省略)while(1) {lv_timer_handler();SDL_Delay(5);}return 0;
}
关键API解析:
lv_xxx_create(parent)
- 创建对象,指定父对象lv_obj_set_size(obj, width, height)
- 设置对象大小lv_obj_set_pos(obj, x, y)
- 设置对象位置lv_obj_center(obj)
- 让对象在父对象中居中
第4章:让界面"活"起来 - 样式系统入门
4.1 为什么需要样式系统?
样式系统让UI设计实现了内容与表现的分离。想象一下,如果没有样式系统,你要修改应用中所有按钮的颜色,就需要遍历每个按钮逐一设置。有了样式系统,你只需要修改样式定义,所有使用该样式的按钮都会自动更新。
4.2 样式的数学模型
在LVGL中,样式可以被看作一个从状态空间到视觉属性的映射函数:
设:
- S = { s 1 , s 2 , . . . , s n } S = \{s_1, s_2, ..., s_n\} S={s1,s2,...,sn} 是对象的所有可能状态集合
- A = { a 1 , a 2 , . . . , a m } A = \{a_1, a_2, ..., a_m\} A={a1,a2,...,am} 是所有可设置的视觉属性集合
那么样式函数 f f f 定义为:
f : S → A f: S \rightarrow A f:S→A
对于每个状态 s i s_i si,样式系统返回对应的属性集合 A i A_i Ai。
4.3 理解样式状态
LVGL对象可以同时处于多个状态,常见状态包括:
状态 | 描述 | 触发条件 |
---|---|---|
LV_STATE_DEFAULT | 默认状态 | 对象创建时的初始状态 |
LV_STATE_PRESSED | 按下状态 | 对象被点击或触摸时 |
LV_STATE_FOCUSED | 获得焦点 | 通过键盘或编码器导航时 |
LV_STATE_DISABLED | 禁用状态 | 对象被禁用时 |
LV_STATE_CHECKED | 选中状态 | 开关、复选框等被选中时 |
状态可以组合使用:
// 组合状态:默认 + 按下
lv_state_t state = LV_STATE_DEFAULT | LV_STATE_PRESSED;
4.4 创建和应用样式
样式创建流程:
代码示例:创建精美的按钮样式
#include <lvgl.h>// ... 省略初始化代码int main(void) {// LVGL初始化(省略)/********************* 1. 创建样式对象********************/static lv_style_t style_btn_default; // 默认状态样式static lv_style_t style_btn_pressed; // 按下状态样式static lv_style_t style_btn_disabled; // 禁用状态样式// 初始化样式对象lv_style_init(&style_btn_default);lv_style_init(&style_btn_pressed);lv_style_init(&style_btn_disabled);/********************* 2. 配置默认状态样式(蓝色渐变按钮)********************/lv_style_set_bg_color(&style_btn_default, lv_color_hex(0x2196F3)); // 主蓝色lv_style_set_bg_grad_color(&style_btn_default, lv_color_hex(0x1976D2)); // 渐变深蓝色lv_style_set_bg_grad_dir(&style_btn_default, LV_GRAD_DIR_VER); // 垂直渐变lv_style_set_radius(&style_btn_default, 12); // 圆角半径lv_style_set_border_width(&style_btn_default, 0); // 无边框lv_style_set_shadow_width(&style_btn_default, 8); // 阴影大小lv_style_set_shadow_color(&style_btn_default, lv_color_hex(0x1A237E)); // 阴影颜色lv_style_set_shadow_ofs_y(&style_btn_default, 4); // 阴影Y偏移// 文字样式lv_style_set_text_color(&style_btn_default, lv_color_white()); // 白色文字/********************* 3. 配置按下状态样式(深蓝色)********************/lv_style_set_bg_color(&style_btn_pressed, lv_color_hex(0x1976D2)); // 深蓝色lv_style_set_bg_grad_color(&style_btn_pressed, lv_color_hex(0x0D47A1)); // 更深的蓝色lv_style_set_shadow_ofs_y(&style_btn_pressed, 2); // 按下时阴影变小lv_style_set_translate_y(&style_btn_pressed, 2); // 按下时下沉效果/********************* 4. 配置禁用状态样式(灰色)********************/lv_style_set_bg_color(&style_btn_disabled, lv_color_hex(0x9E9E9E)); // 灰色lv_style_set_bg_grad_color(&style_btn_disabled, lv_color_hex(0x757575)); // 深灰色lv_style_set_text_color(&style_btn_disabled, lv_color_hex(0xE0E0E0)); // 浅灰色文字/********************* 5. 创建按钮并应用样式********************/lv_obj_t * btn1 = lv_btn_create(lv_scr_act());lv_obj_set_size(btn1, 120, 50);lv_obj_set_pos(btn1, 50, 50);// 应用样式到不同状态lv_obj_add_style(btn1, &style_btn_default, LV_STATE_DEFAULT);lv_obj_add_style(btn1, &style_btn_pressed, LV_STATE_PRESSED);lv_obj_add_style(btn1, &style_btn_disabled, LV_STATE_DISABLED);// 添加按钮文字lv_obj_t * label1 = lv_label_create(btn1);lv_label_set_text(label1, "启用状态");lv_obj_center(label1);/********************* 6. 创建禁用状态的按钮用于对比********************/lv_obj_t * btn2 = lv_btn_create(lv_scr_act());lv_obj_set_size(btn2, 120, 50);lv_obj_set_pos(btn2, 200, 50);// 应用相同的样式lv_obj_add_style(btn2, &style_btn_default, LV_STATE_DEFAULT);lv_obj_add_style(btn2, &style_btn_pressed, LV_STATE_PRESSED);lv_obj_add_style(btn2, &style_btn_disabled, LV_STATE_DISABLED);// 设置按钮为禁用状态lv_obj_add_state(btn2, LV_STATE_DISABLED);// 添加按钮文字lv_obj_t * label2 = lv_label_create(btn2);lv_label_set_text(label2, "禁用状态");lv_obj_center(label2);// 主循环(省略)while(1) {lv_timer_handler();SDL_Delay(5);}return 0;
}
4.5 常用样式属性详解
LVGL提供了丰富的样式属性,主要分为以下几类:
背景属性
lv_style_set_bg_color(style, color); // 背景颜色
lv_style_set_bg_grad_color(style, color); // 渐变颜色
lv_style_set_bg_grad_dir(style, dir); // 渐变方向
lv_style_set_bg_main_stop(style, value); // 主颜色停止点
lv_style_set_bg_grad_stop(style, value); // 渐变颜色停止点
lv_style_set_bg_opa(style, opa); // 背景透明度 (0-255)
边框属性
lv_style_set_border_color(style, color); // 边框颜色
lv_style_set_border_width(style, width); // 边框宽度
lv_style_set_border_opa(style, opa); // 边框透明度
lv_style_set_border_side(style, side); // 边框边 (LEFT/RIGHT/TOP/BOTTOM/ALL)
轮廓属性
lv_style_set_outline_color(style, color); // 轮廓颜色
lv_style_set_outline_width(style, width); // 轮廓宽度
lv_style_set_outline_pad(style, value); // 轮廓填充
阴影属性
lv_style_set_shadow_color(style, color); // 阴影颜色
lv_style_set_shadow_width(style, width); // 阴影大小
lv_style_set_shadow_ofs_x(style, value); // 阴影X偏移
lv_style_set_shadow_ofs_y(style, value); // 阴影Y偏移
几何属性
lv_style_set_width(style, value); // 宽度
lv_style_set_height(style, value); // 高度
lv_style_set_x(style, value); // X坐标
lv_style_set_y(style, value); // Y坐标
lv_style_set_align(style, align); // 对齐方式
lv_style_set_transform_width(style, value); // 变换宽度
lv_style_set_transform_height(style, value); // 变换高度
文字属性
lv_style_set_text_color(style, color); // 文字颜色
lv_style_set_text_font(style, font); // 字体
lv_style_set_text_opa(style, opa); // 文字透明度
lv_style_set_text_letter_space(style, value); // 字母间距
lv_style_set_text_line_space(style, value); // 行间距
第5章:基础控件(一)- 标签、按钮与基础容器
5.1 标签:文字的载体
标签是LVGL中最基本的文本显示控件。
核心功能:
- 显示静态或动态文本
- 支持文本换行和滚动
- 支持文本对齐方式
- 支持长文本模式
数学表达:
标签的渲染可以看作一个文本布局函数:
R = L ( T , F , A , M ) R = L(T, F, A, M) R=L(T,F,A,M)
其中:
- R R R = 渲染结果
- T T T = 文本内容
- F F F = 字体属性
- A A A = 对齐方式
- M M M = 长文本模式
代码示例:标签的各种用法
// 创建基础标签
lv_obj_t * label1 = lv_label_create(lv_scr_act());
lv_label_set_text(label1, "这是基础标签");
lv_obj_set_pos(label1, 10, 10);// 创建长文本标签(自动换行)
lv_obj_t * label2 = lv_label_create(lv_scr_act());
lv_obj_set_size(label2, 200, 100); // 必须设置大小才能换行
lv_obj_set_pos(label2, 10, 40);
lv_label_set_text(label2, "这是一个很长的文本,它会在标签边界处自动换行,确保所有内容都能正确显示。");
lv_label_set_long_mode(label2, LV_LABEL_LONG_WRAP); // 换行模式// 创建滚动文本标签
lv_obj_t * label3 = lv_label_create(lv_scr_act());
lv_obj_set_size(label3, 150, 25);
lv_obj_set_pos(label3, 10, 150);
lv_label_set_text(label3, "这是一个会水平滚动的文本,当文本过长时...");
lv_label_set_long_mode(label3, LV_LABEL_LONG_SCROLL); // 滚动模式// 创建居中对齐标签
lv_obj_t * label4 = lv_label_create(lv_scr_act());
lv_obj_set_size(label4, 200, 30);
lv_obj_set_pos(label4, 10, 190);
lv_label_set_text(label4, "居中对齐的文本");
lv_obj_set_style_text_align(label4, LV_TEXT_ALIGN_CENTER, 0);// 动态更新文本
static int counter = 0;
lv_obj_t * dynamic_label = lv_label_create(lv_scr_act());
lv_obj_set_pos(dynamic_label, 10, 230);// 在定时器中更新文本
lv_timer_t * timer = lv_timer_create([](lv_timer_t * timer) {static int count = 0;lv_label_set_text_fmt((lv_obj_t *)timer->user_data, "计数器: %d", count++);
}, 1000, dynamic_label);
5.2 按钮:交互的基础
按钮是最常用的交互控件,它本身是一个容器,通常内部包含标签。
按钮状态机:
代码示例:创建功能性按钮
// 创建普通按钮
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 100, 40);
lv_obj_set_pos(btn, 250, 50);// 添加按钮文字
lv_obj_t * btn_label = lv_label_create(btn);
lv_label_set_text(btn_label, "点击我");
lv_obj_center(btn_label);// 创建开关式按钮
lv_obj_t * toggle_btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(toggle_btn, 100, 40);
lv_obj_set_pos(toggle_btn, 250, 100);
lv_obj_add_flag(toggle_btn, LV_OBJ_FLAG_CHECKABLE); // 使按钮可切换lv_obj_t * toggle_label = lv_label_create(toggle_btn);
lv_label_set_text(toggle_label, "开关");
lv_obj_center(toggle_label);
5.3 基础容器:布局的基石
容器本身没有特定的外观,主要用于组织和布局子对象。
容器的核心作用:
- 分组相关控件
- 简化布局管理
- 控制子对象的可见性和位置
代码示例:使用容器组织界面
// 创建主容器
lv_obj_t * container = lv_obj_create(lv_scr_act());
lv_obj_set_size(container, 280, 180);
lv_obj_set_pos(container, 10, 270);
lv_obj_set_flex_flow(container, LV_FLEX_FLOW_COLUMN); // 垂直排列
lv_obj_set_style_pad_all(container, 10, 0); // 内边距// 在容器内创建标题标签
lv_obj_t * title = lv_label_create(container);
lv_label_set_text(title, "设置面板");
lv_obj_set_style_text_font(title, &lv_font_montserrat_16, 0);// 创建水平排列的按钮组
lv_obj_t * btn_container = lv_obj_create(container);
lv_obj_set_size(btn_container, lv_pct(100), 50);
lv_obj_set_flex_flow(btn_container, LV_FLEX_FLOW_ROW); // 水平排列
lv_obj_set_style_bg_opa(btn_container, LV_OPA_0, 0); // 透明背景
lv_obj_set_style_border_width(btn_container, 0, 0); // 无边框// 在按钮容器中添加多个按钮
for(int i = 0; i < 3; i++) {lv_obj_t * btn = lv_btn_create(btn_container);lv_obj_set_flex_grow(btn, 1); // 等分宽度lv_obj_t * label = lv_label_create(btn);lv_label_set_text_fmt(label, "选项 %d", i + 1);lv_obj_center(label);
}
第6章:基础控件(二)- 滑块、开关、进度条
6.1 滑块:精确的数值输入
滑块允许用户通过拖动来选择特定范围内的数值。
滑块的值映射:
滑块将一个物理位置映射到一个数值范围内:
v a l u e = m i n + p o s i t i o n m a x _ p o s i t i o n × ( m a x − m i n ) value = min + \frac{position}{max\_position} \times (max - min) value=min+max_positionposition×(max−min)
其中:
- v a l u e value value = 当前值
- m i n min min, m a x max max = 值范围
- p o s i t i o n position position = 滑块当前位置
- m a x _ p o s i t i o n max\_position max_position = 滑块最大可移动范围
代码示例:创建多功能滑块
// 创建水平滑块
lv_obj_t * slider1 = lv_slider_create(lv_scr_act());
lv_obj_set_size(slider1, 200, 20);
lv_obj_set_pos(slider1, 400, 50);
lv_slider_set_range(slider1, 0, 100); // 设置范围 0-100
lv_slider_set_value(slider1, 30, LV_ANIM_OFF); // 设置初始值// 创建垂直滑块
lv_obj_t * slider2 = lv_slider_create(lv_scr_act());
lv_obj_set_size(slider2, 20, 150);
lv_obj_set_pos(slider2, 450, 80);
lv_slider_set_range(slider2, 0, 100);
lv_slider_set_value(slider2, 70, LV_ANIM_OFF);// 显示滑块值的标签
lv_obj_t * slider_label = lv_label_create(lv_scr_act());
lv_obj_set_pos(slider_label, 400, 80);// 滑块事件处理
lv_obj_add_event_cb(slider1, [](lv_event_t * e) {lv_obj_t * slider = lv_event_get_target(e);int32_t value = lv_slider_get_value(slider);// 更新标签显示lv_label_set_text_fmt((lv_obj_t *)lv_event_get_user_data(e), "值: %d%%", value);
}, LV_EVENT_VALUE_CHANGED, slider_label);
6.2 开关:二进制状态选择
开关用于表示开/关、是/否等二进制状态。
开关的布尔逻辑:
s t a t e = { true if checked false if unchecked state = \begin{cases} \text{true} & \text{if checked} \\ \text{false} & \text{if unchecked} \end{cases} state={truefalseif checkedif unchecked
代码示例:创建状态开关
// 创建开关
lv_obj_t * sw = lv_switch_create(lv_scr_act());
lv_obj_set_pos(sw, 400, 150);// 创建开关状态标签
lv_obj_t * sw_label = lv_label_create(lv_scr_act());
lv_obj_set_pos(sw_label, 480, 150);
lv_label_set_text(sw_label, "关");// 开关事件处理
lv_obj_add_event_cb(sw, [](lv_event_t * e) {lv_obj_t * sw = lv_event_get_target(e);bool state = lv_obj_has_state(sw, LV_STATE_CHECKED);lv_label_set_text((lv_obj_t *)lv_event_get_user_data(e), state ? "开" : "关");
}, LV_EVENT_VALUE_CHANGED, sw_label);
6.3 进度条:操作反馈与状态显示
进度条用于显示操作的进度或系统的状态。
进度计算:
p r o g r e s s = c u r r e n t − m i n m a x − m i n × 100 % progress = \frac{current - min}{max - min} \times 100\% progress=max−mincurrent−min×100%
代码示例:创建动态进度条
// 创建进度条
lv_obj_t * progress_bar = lv_bar_create(lv_scr_act());
lv_obj_set_size(progress_bar, 200, 20);
lv_obj_set_pos(progress_bar, 400, 200);
lv_bar_set_range(progress_bar, 0, 100); // 设置范围
lv_bar_set_value(progress_bar, 0, LV_ANIM_OFF);// 进度标签
lv_obj_t * progress_label = lv_label_create(lv_scr_act());
lv_obj_set_pos(progress_label, 400, 230);
lv_label_set_text(progress_label, "0%");// 模拟进度更新
static int progress = 0;
lv_timer_t * progress_timer = lv_timer_create([](lv_timer_t * timer) {if(progress >= 100) {lv_timer_del(timer);return;}progress += 2;lv_bar_set_value((lv_obj_t *)timer->user_data, progress, LV_ANIM_ON);// 更新标签lv_label_set_text_fmt(progress_label, "%d%%", progress);
}, 100, progress_bar);
6.4 综合实战:创建简易调光器界面
现在让我们综合运用所学知识,创建一个完整的调光器控制面板:
void create_dimmer_interface() {// 创建主容器lv_obj_t * container = lv_obj_create(lv_scr_act());lv_obj_set_size(container, 300, 200);lv_obj_align(container, LV_ALIGN_TOP_RIGHT, -20, 20);lv_obj_set_style_bg_color(container, lv_color_hex(0x2C3E50), 0);lv_obj_set_style_radius(container, 15, 0);// 标题lv_obj_t * title = lv_label_create(container);lv_label_set_text(title, "LED调光器");lv_obj_set_style_text_color(title, lv_color_white(), 0);lv_obj_set_style_text_font(title, &lv_font_montserrat_18, 0);lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 15);// 亮度滑块lv_obj_t * brightness_slider = lv_slider_create(container);lv_obj_set_size(brightness_slider, 200, 20);lv_obj_align(brightness_slider, LV_ALIGN_TOP_MID, 0, 60);lv_slider_set_range(brightness_slider, 0, 100);lv_slider_set_value(brightness_slider, 75, LV_ANIM_OFF);// 亮度标签lv_obj_t * brightness_label = lv_label_create(container);lv_obj_set_style_text_color(brightness_label, lv_color_white(), 0);lv_obj_align_to(brightness_label, brightness_slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 10);// 开关控制lv_obj_t * power_switch = lv_switch_create(container);lv_obj_align(power_switch, LV_ALIGN_TOP_MID, 0, 120);lv_obj_t * power_label = lv_label_create(container);lv_label_set_text(power_label, "电源");lv_obj_set_style_text_color(power_label, lv_color_white(), 0);lv_obj_align_to(power_label, power_switch, LV_ALIGN_OUT_LEFT_MID, -10, 0);// 事件处理:更新亮度显示lv_obj_add_event_cb(brightness_slider, [](lv_event_t * e) {lv_obj_t * slider = lv_event_get_target(e);int32_t value = lv_slider_get_value(slider);lv_label_set_text_fmt((lv_obj_t *)lv_event_get_user_data(e), "亮度: %d%%", value);}, LV_EVENT_VALUE_CHANGED, brightness_label);// 初始化亮度显示lv_event_send(brightness_slider, LV_EVENT_VALUE_CHANGED, NULL);// 事件处理:开关状态lv_obj_add_event_cb(power_switch, [](lv_event_t * e) {lv_obj_t * sw = lv_event_get_target(e);bool enabled = lv_obj_has_state(sw, LV_STATE_CHECKED);// 这里可以添加实际的LED控制逻辑printf("LED %s\n", enabled ? "开启" : "关闭");}, LV_EVENT_VALUE_CHANGED, NULL);
}
在main函数中调用:
int main(void) {// ... 初始化代码create_dimmer_interface();// ... 主循环
}
本章总结与挑战
恭喜!你已经掌握了LVGL的核心构建块。你现在能够:
- 理解LVGL的面向对象模型和对象树概念
- 创建和配置丰富的样式系统,实现精美的UI效果
- 熟练使用基础控件:标签、按钮、容器、滑块、开关、进度条
- 组合多个控件创建功能完整的用户界面
小挑战(巩固练习):
- 样式进阶:为调光器界面添加按下状态的样式效果,让按钮和滑块在交互时有视觉反馈。
- 布局优化:使用容器和Flex布局重新组织调光器界面,使其在不同屏幕尺寸下都能良好显示。
- 功能扩展:在调光器界面中添加颜色选择功能,使用多个滑块分别控制RGB值。
- 状态联动:实现当电源开关关闭时,自动禁用亮度滑块并将其值设置为0。
在下一篇文章中,我们将深入探讨事件系统和高级布局技术(Flex和Grid),让你能够创建更加动态和响应式的用户界面。
研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)