尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

X-TRACK 界面布局实战:LVGL 坐标、尺寸与布局系统详解

X-TRACK 界面布局实战:LVGL 坐标、尺寸与布局系统详解 智能硬件嵌入式硬件开发【免费下载链接】X-TRACKA GPS bicycle speedometer that supports offline maps and track recording项目地址https://gitcode.com/gh_mirrors/xt/X-TRACK点击查看免费下载导读X-TRACK 是一个基于 LVGL 8.3 构建的 GPS 自行车码表固件其全部界面码表盘、地图、状态栏、系统信息页都由 LVGL 的对象坐标系统驱动。本文以 LVGL 官方文档中的《Positions, sizes, and layouts》章节为主体结合 X-TRACK 仓库内 DialplateView.cpp 等真实界面代码系统讲解 LVGL 的盒模型、单位、定位、对齐、尺寸、样式坐标、平移、变换与 Flex/Grid 布局。读完本文你将掌握用像素、百分比和LV_SIZE_CONTENT精确排布界面元素的能力并能直接读懂 X-TRACK 各页面源码的布局逻辑。一、坐标系统总览仿 CSS 但更精简LVGL 的坐标概念借鉴了 CSS但并非完整实现而是实现了一个可比子集。从 lvgl.h 可以看到X-TRACK 内置的 LVGL 版本为 8.3.0。其坐标系统可以概括为显式设置的坐标尺寸、位置、布局等都存储在样式中支持min-width、max-width、min-height、max-height限制提供像素、百分比、内容三种单位x0; y0表示父对象内容区的左上角还需加上父对象的左/上 padding 与边框宽度width/height表示完整的外包围盒尺寸内容区比它更小减去 padding 与边框支持 flexbox 与 grid 布局的子集。本文围绕 X-TRACK 中真实使用的定位 API 展开读者可对照 lv_obj_pos.c坐标系统核心实现与 lv_obj_pos.hAPI 声明阅读。二、三种单位像素、百分比与 LV_SIZE_CONTENT像素px整数一律表示像素例如lv_obj_set_x(btn, 10);百分比%百分比表示对象或其父对象尺寸的比例取决于具体属性通过lv_pct(value)将数值转换为百分比值lv_obj_set_width(btn, lv_pct(50)); /* 宽度 父内容区宽度的 50% */LV_SIZE_CONTENT类似 CSS autoLV_SIZE_CONTENT是特殊值让对象宽/高自动包住所有子对象lv_obj_set_width(btn, LV_SIZE_CONTENT);从源码看LV_SIZE_CONTENT的解析位于 lv_obj_pos.c当检测到宽度值为LV_SIZE_CONTENT时会调用calc_content_width(obj)计算子对象总宽当检测到百分比时则用w (LV_COORD_GET_PCT(w) * parent_w) / 100换算成像素。这也解释了为什么百分比依赖父对象内容区尺寸。三、盒模型Boxing modelLVGL 遵循 CSS 的border-box模型。一个对象的盒子由四部分组成部分含义bounding box外包围盒元素的宽高border width边框宽度边框占据的宽度padding内边距对象边缘与子对象之间的空间content内容区外包围盒减去边框宽度和 padding 后的区域边框绘制在外包围盒内部边框内部 LVGL 会保留一个padding 边距用于放置子对象outline轮廓线则绘制在外包围盒外部。理解这一点对 X-TRACK 的开发非常关键例如 DialplateView.cpp 中lv_obj_set_size(cont, LV_HOR_RES, 142)设置的是外包围盒尺寸子对象的实际可用区域还要减去该容器的 padding 与边框。四、坐标计算的延迟刷新机制重要注意事项LVGL 不会立即重算所有坐标变化这是为了提升性能。对象会被标记为 dirty在重绘屏幕前 LVGL 才检查并刷新它们的位置、尺寸与布局。因此如果你刚刚修改了坐标就需要读取坐标值必须强制重算lv_obj_update_layout(obj);由于尺寸和位置可能依赖父对象或布局lv_obj_update_layout会重算obj所在屏幕上所有对象的坐标。其实现位于 lv_obj_pos.c函数内部循环检查屏幕级标志scr-scr_layout_inv直到所有布局失效都被处理完毕同时通过mutex防止递归重入。此外lv_obj_mark_layout_as_dirty 展示了标记失效并唤醒刷新定时器的完整链路置位obj-layout_inv与scr-scr_layout_inv再lv_timer_resume(disp-refr_timer)让屏幕刷新定时器开始工作。X-TRACK 中 DialplateView.cpp 正是先调用lv_obj_get_y()读取动画目标坐标、再据此构造动画时间线说明先刷新布局再取坐标是可靠的编码顺序。五、样式与坐标的绑定关系文档明确指出所有坐标相关的属性在底层都是样式属性。调用lv_obj_set_x(obj, 20)时LVGL 实际把x20存入对象的 local style。从源码可以印证这一机制lv_obj_set_x 的实现先读取本地样式中的LV_STYLE_X属性若值不同则调用lv_obj_set_style_x(obj, x, 0)写入 local stylelv_obj_set_y同理。也就是说简单函数只是对样式 API 的封装。因此有一个必须警惕的坑移除样式会同时移除先前设置的坐标。/* obj1 的尺寸最终会被重置为默认值 */ lv_obj_set_size(obj1, 200, 100); /* 现在 obj1 是 200x100 */ lv_obj_remove_style_all(obj1); /* 这会移除已设置的尺寸 */ /* obj2 最终保持 200x100 */ lv_obj_remove_style_all(obj2); lv_obj_set_size(obj2, 200, 100);X-TRACK 各页面大量使用lv_obj_remove_style_all(cont)先清空默认样式再自定义外观如 DialplateView.cpp这正是基于对坐标即样式这一机制的掌握——清样式后再设尺寸/位置顺序至关重要。六、位置Position6.1 基础设置lv_obj_set_x(obj, 10); /* 单独设置 x */ lv_obj_set_y(obj, 20); /* 单独设置 y */ lv_obj_set_pos(obj, 10, 20); /* 一次设置 x 和 y */默认情况下x/y 从父对象内容区的左上角开始计算。例如父对象四周各有 5 像素 padding上述代码会把obj放在 (15, 25)——因为内容区从 padding 之后才开始。百分比也是相对父对象内容区尺寸计算lv_obj_set_x(btn, lv_pct(10)); /* x 父内容区宽度的 10% */从源码 lv_obj_set_pos 可以看到它就是对lv_obj_set_x与lv_obj_set_y的组合调用。6.2 对齐Align有时需要改变定位原点默认是左上角。比如把原点改为右下角那么 (0,0) 就意味着对齐右下角lv_obj_set_align(obj, align); /* 改变对齐方式 */ lv_obj_align(obj, align, x, y); /* 改变对齐方式并设置新坐标 */可用的对齐选项LV_ALIGN_TOP_LEFT、LV_ALIGN_TOP_MID、LV_ALIGN_TOP_RIGHTLV_ALIGN_BOTTOM_LEFT、LV_ALIGN_BOTTOM_MID、LV_ALIGN_BOTTOM_RIGHTLV_ALIGN_LEFT_MID、LV_ALIGN_RIGHT_MIDLV_ALIGN_CENTER将子对象居中到父对象非常常见因此有专用函数lv_obj_center(obj); /* 等价写法 */ lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);父对象尺寸变化时子对象已设置的对齐和位置会自动更新。除对齐到父对象外还可以对齐到任意参考对象lv_obj_align_to(obj_to_align, reference_obj, align, x, y);此时还可使用以下对齐到参考对象外部的选项LV_ALIGN_OUT_TOP_LEFT、LV_ALIGN_OUT_TOP_MID、LV_ALIGN_OUT_TOP_RIGHTLV_ALIGN_OUT_BOTTOM_LEFT、LV_ALIGN_OUT_BOTTOM_MID、LV_ALIGN_OUT_BOTTOM_RIGHTLV_ALIGN_OUT_LEFT_TOP、LV_ALIGN_OUT_LEFT_MID、LV_ALIGN_OUT_LEFT_BOTTOMLV_ALIGN_OUT_RIGHT_TOP、LV_ALIGN_OUT_RIGHT_MID、LV_ALIGN_OUT_RIGHT_BOTTOM例如把标签放到按钮上方并水平居中lv_obj_align_to(label, btn, LV_ALIGN_OUT_TOP_MID, 0, -10);注意与lv_obj_align()不同lv_obj_align_to()在对象坐标或参考对象坐标改变后不会自动重新对齐。从源码 lv_obj_align_to 可见它是即时计算目标位置并一次性设置坐标的。X-TRACK 中 DialplateView.cpp 用lv_obj_align_to(label, ui.topInfo.labelSpeed, LV_ALIGN_OUT_BOTTOM_MID, 0, 8)把 km/h 单位标签放在速度数字下方 8 像素处是OUT_*系列对齐的典型实战用法。七、尺寸Size7.1 基础设置lv_obj_set_width(obj, 200); /* 单独设置宽度 */ lv_obj_set_height(obj, 100); /* 单独设置高度 */ lv_obj_set_size(obj, 200, 100); /* 一次设置宽高 */百分比基于父对象内容区尺寸例如把高度设为屏幕高度lv_obj_set_height(obj, lv_pct(100));7.2 LV_SIZE_CONTENT 的细节LV_SIZE_CONTENT表示对象在对应方向上的尺寸等于其子对象尺寸之和。注意只有位于右侧和下方的子对象会被计入位于顶部和左侧的子对象会被裁剪这一限制让行为更可预测。带有LV_OBJ_FLAG_HIDDEN或LV_OBJ_FLAG_FLOATING标志的对象会被LV_SIZE_CONTENT计算忽略。7.3 设置/读取内容区尺寸上述函数设置的是外包围盒尺寸但也可以直接设置内容区尺寸此时外包围盒会加上 padding 而变大lv_obj_set_content_width(obj, 50); /* 实际宽度 padding left 50 padding right */ lv_obj_set_content_height(obj, 30); /* 实际高度 padding top 30 padding bottom */读取外包围盒与内容区尺寸lv_coord_t w lv_obj_get_width(obj); lv_coord_t h lv_obj_get_height(obj); lv_coord_t content_w lv_obj_get_content_width(obj); lv_coord_t content_h lv_obj_get_content_height(obj);八、用样式设置坐标Using styles位置、尺寸、对齐属性在底层都是样式属性前述简单函数只是把样式代码封装隐藏。用样式设置坐标有显著优势便于给多个对象统一设置宽高等属性例如让所有滑块都是 100x10 像素只需修改一处即可全局生效值可以被其他样式部分覆盖例如style_btn默认 100x50而style_full_width只覆盖宽度对象可随状态不同而拥有不同的位置/尺寸如LV_STATE_DEFAULT下宽 100px、LV_STATE_PRESSED下宽 120px配合样式过渡transition让坐标变化更平滑。示例——用样式设置对象尺寸static lv_style_t style; lv_style_init(style); lv_style_set_width(style, 100); lv_obj_t * btn lv_btn_create(lv_scr_act()); lv_obj_add_style(btn, style, LV_PART_MAIN);X-TRACK 的页面大量使用这一模式例如 DialplateView.cpp 用lv_obj_set_style_bg_opa、lv_obj_set_style_bg_color、lv_obj_set_style_radius直接定义容器的视觉样式与lv_obj_set_size、lv_obj_set_y这些简单函数混合使用二者底层统一作用于 style 属性互不冲突。九、平移Translation场景三个按钮并排希望按下时按钮略微上移。一种方式是给按下状态设置新的 Y 坐标static lv_style_t style_normal; lv_style_init(style_normal); lv_style_set_y(style_normal, 100); static lv_style_t style_pressed; lv_style_init(style_pressed); lv_style_set_y(style_pressed, 80); lv_obj_add_style(btn1, style_normal, LV_STATE_DEFAULT); lv_obj_add_style(btn1, style_pressed, LV_STATE_PRESSED); lv_obj_add_style(btn2, style_normal, LV_STATE_DEFAULT); lv_obj_add_style(btn2, style_pressed, LV_STATE_PRESSED); lv_obj_add_style(btn3, style_normal, LV_STATE_DEFAULT); lv_obj_add_style(btn3, style_pressed, LV_STATE_PRESSED);这样写不够灵活因为按下坐标被硬编码了。若按钮不在 y100 处style_pressed就不起作用。翻译translate可解决此问题——它以对象当前位置为基准static lv_style_t style_normal; lv_style_init(style_normal); lv_style_set_y(style_normal, 100); static lv_style_t style_pressed; lv_style_init(style_pressed); lv_style_set_translate_y(style_pressed, -20); lv_obj_add_style(btn1, style_normal, LV_STATE_DEFAULT); lv_obj_add_style(btn1, style_pressed, LV_STATE_PRESSED); lv_obj_add_style(btn2, style_normal, LV_STATE_DEFAULT); lv_obj_add_style(btn2, style_pressed, LV_STATE_PRESSED); lv_obj_add_style(btn3, style_normal, LV_STATE_DEFAULT); lv_obj_add_style(btn3, style_pressed, LV_STATE_PRESSED);翻译从对象当前位置开始应用也可以使用百分比——注意百分比相对的是对象自身尺寸而非父对象例如lv_pct(50)会把对象移动自身宽/高的一半。翻译在布局计算之后应用因此即使是布局排布出来的对象也能被平移。翻译真实移动对象意味着滚动条和LV_SIZE_CONTENT对象都会对位置变化作出响应。十、变换Transformation与位置类似对象尺寸也可以相对当前尺寸变化。变换后的宽高加在对象两侧10px 的变换宽度会让对象实际加宽 2x1020px。与平移不同尺寸变换不会让对象真的变大——滚动条、布局和LV_SIZE_CONTENT都不会对变换后的尺寸作出反应它只是视觉效果。按下时放大按钮static lv_style_t style_pressed; lv_style_init(style_pressed); lv_style_set_transform_width(style_pressed, 10); lv_style_set_transform_height(style_pressed, 10); lv_obj_add_style(btn, style_pressed, LV_STATE_PRESSED);十一、最小/最大尺寸Min and Max size与 CSS 类似LVGL 支持min-width、max-width、min-height、max-height用来阻止对象尺寸小于/大于这些限制值。当尺寸由百分比或LV_SIZE_CONTENT决定时尤其有用static lv_style_t style_max_height; lv_style_init(style_max_height); lv_style_set_y(style_max_height, 200); lv_obj_set_height(obj, lv_pct(100)); lv_obj_add_style(obj, style_max_height, LV_STATE_DEFAULT); /* 把高度限制在 200px */注意上述示例是文档原样给出的演示代码实际限制高度应使用lv_style_set_max_height这里以原文档为准保留示例。限制值也可以用百分比相对父对象内容区尺寸static lv_style_t style_max_height; lv_style_init(style_max_height); lv_style_set_y(style_max_height, lv_pct(50)); lv_obj_set_height(obj, lv_pct(100)); lv_obj_add_style(obj, style_max_height, LV_STATE_DEFAULT); /* 限制高度为父对象高度的一半 */十二、布局Layout12.1 概述布局会更新对象子元素的位置和尺寸可自动将子元素排成一行/一列或更复杂的形态。布局设置的位置和尺寸会覆盖普通的 x、y、width、height 设置。所有布局共用一个入口函数lv_obj_set_layout(obj, LAYOUT_NAME);父对象和子对象的进一步配置请参考对应布局的文档。12.2 内置布局Flexbox 与 GridLVGL 内置两种强大的布局都深受同名 CSS 布局启发FlexboxGridX-TRACK 在底部信息栏使用了 Flex 布局见 DialplateView.cpp代码调用lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW_WRAP)让 4 组子信息AVG/Time/Trip/Calorie横向换行排列并用lv_obj_set_flex_align(cont, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER)让它们在主轴方向上均匀分布、交叉轴居中。这也是本文档所述 flexbox 子集 在真实产品界面中的直接体现。12.3 影响布局的标志Flags以下标志会影响对象在布局中的行为LV_OBJ_FLAG_HIDDEN隐藏对象在布局计算中被忽略。LV_OBJ_FLAG_IGNORE_LAYOUT对象被布局完全忽略其坐标可照常手动设置。LV_OBJ_FLAG_FLOATING同LV_OBJ_FLAG_IGNORE_LAYOUT但带LV_OBJ_FLAG_FLOATING的对象还会在LV_SIZE_CONTENT计算中被忽略。标志通过lv_obj_add/clear_flag(obj, FLAG)添加/移除。12.4 自定义布局LVGL 可自由扩展自定义布局uint32_t MY_LAYOUT; MY_LAYOUT lv_layout_register(my_layout_update, user_data); void my_layout_update(lv_obj_t * obj, void * user_data) { /* 当需要重新定位/调整 obj 的子对象时会被自动调用 */ }注册的实现见 lv_obj_pos.c每注册一个布局layout_cnt自增回调与user_data被存入全局布局描述符列表_lv_layout_list返回值从 1 开始递增跳过 0 索引。自定义样式属性同样可以注册并在更新回调中读取使用uint32_t MY_PROP; LV_STYLE_MY_PROP lv_style_register_prop(); static inline void lv_style_set_my_prop(lv_style_t * style, uint32_t value) { lv_style_value_t v { .num (int32_t)value }; lv_style_set_prop(style, LV_STYLE_MY_PROP, v); }十三、X-TRACK 实战要点小结结合仓库源码将本文核心知识点落到 X-TRACK 的开发实践中先清样式再设坐标X-TRACK 页面普遍先lv_obj_remove_style_all()再设置尺寸与位置DialplateView.cpp因为坐标本身就是样式属性顺序颠倒会丢失坐标设置。取坐标前先刷新布局若刚改过坐标就要读取如构造动画时间线先调用lv_obj_update_layout(obj)再使用lv_obj_get_y()等读取函数DialplateView.cpp。用 OUT_对齐做相对排版*lv_obj_align_to(label, ref, LV_ALIGN_OUT_TOP/BOTTOM_MID, x, y)是实现标签挂在某控件旁边最直观的方式但要注意它不会随参考对象自动重排DialplateView.cpp。布局优先于手动坐标一旦使用lv_obj_set_flex_flow/lv_obj_set_flex_align等布局配置子对象的坐标与尺寸会被布局覆盖此时不要再手动设置子对象位置DialplateView.cpp。平移与变换的取舍需要真的移动影响滚动与LV_SIZE_CONTENT用translate只需要视觉放大/偏移不影响布局用transform。以上全部结论均可在 X-TRACK 仓库内验证坐标系统核心实现见 lv_obj_pos.c 与 lv_obj_pos.h页面级实战代码见 DialplateView.cppFlex 布局的更多用法可参考 SystemInfosView.cpp。赞分享智能硬件嵌入式硬件开发【免费下载链接】X-TRACKA GPS bicycle speedometer that supports offline maps and track recording项目地址https://gitcode.com/gh_mirrors/xt/X-TRACK点击查看免费下载相关推荐揭秘ruadapt-Qwen2.5-1.5B-ft-openmind的训练原理基于MIRACL数据集的优化策略揭秘ruadapt Qwen2.5 1.5B ft openmind的训练原理基于MIRACL数据集的优化策略 ruadapt Qwen2.5 1.5B ftTexture 布局 API 尺寸体系详解ASDimension、ASSizeRange 与 flexBasis/sizeRange 实战Texture 布局 API 尺寸体系详解ASDimension、ASSizeRange 与 flexBasis/sizeRange 实战 导读 本文围绕 T移动开发UI组件Bokeh项目布局与尺寸控制实战sizing_mode参数详解Bokeh项目布局与尺寸控制实战sizing_mode参数详解 引言为什么需要sizing_mode 在数据可视化项目中布局管理往往是最令人头疼的问题之数据可视化图表库上一篇网易云 NCM 转 MP3 三分钟搞定免费开源 ncmdump 拖一下就能解锁下一篇QMC格式解密免费工具QMCDecode完整上手指南一键还原QQ音乐加密歌曲创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表