4、UI组件深度解析(下):布局容器与高级控件实战
这一章,我们继续啃UI组件这块硬骨头。上回聊了基础控件,这次聚焦在布局容器和几个比较“重”的交互组件上。说实话,ListLayout、GridLayout这些容器,用好了能让你的界面自适应能力上一个台阶;而ProgressBar、Slider和自定义键盘,则是提升用户体验的关键点。
我个人习惯把布局容器比作“乐高底板”——它们不直接显示内容,但决定了上面每个零件怎么排、怎么动。你想想看,如果每个控件都写死坐标,换一个屏幕尺寸就得重来,那得多痛苦?
4.1 ListLayout:纵向/横向列表的利器
ListLayout,说白了就是一个自动排列子控件的容器。你往里面add一个控件,它就自动往后排。方向可以设成VERTICAL(纵向)或HORIZONTAL(横向)。
我在项目中遇到过一个问题:用ListLayout做菜单列表,每个菜单项高度不一样,结果间距全乱了。后来发现是没设置setMargins和setPadding。这两个属性控制子控件之间的间距和容器边缘的留白,一定要记得配。
// 创建一个纵向ListLayout
ListLayout listLayout;
listLayout.setDirection(ListLayout::VERTICAL);
listLayout.setPadding(10); // 容器内边距10像素
listLayout.setMargins(5, 5); // 子控件之间间距5像素
// 添加三个文本控件
TextArea text1, text2, text3;
text1.setTypedText(TypedText(T_MENU_ITEM_1));
text2.setTypedText(TypedText(T_MENU_ITEM_2));
text3.setTypedText(TypedText(T_MENU_ITEM_3));
listLayout.add(text1);
listLayout.add(text2);
listLayout.add(text3);
setClickAction,这样事件处理更清晰。
4.2 GridLayout:网格布局,规整又高效
GridLayout比ListLayout多了一个维度。你可以指定行数和列数,子控件会自动填充到网格里。适合做图标菜单、仪表盘、参数设置面板这类规整的界面。
嗯,这里要注意:GridLayout的setGrid方法需要传入行数和列数。如果你设成3行4列,那最多放12个子控件。放多了会怎样?它会报错,不会自动换行。我刚开始用的时候就踩过这个坑。
// 创建一个3行4列的GridLayout
GridLayout gridLayout;
gridLayout.setGrid(3, 4); // 3行4列
gridLayout.setPadding(8);
gridLayout.setMargins(4, 4);
// 添加12个图标按钮
for (int i = 0; i < 12; i++) {
Button* btn = new Button();
btn->setBitmaps(Bitmap(BTN_ICON_1 + i), Bitmap(BTN_ICON_1_PRESSED + i));
gridLayout.add(*btn);
}
| 布局容器 | 排列方式 | 适用场景 | 注意事项 |
|---|---|---|---|
| ListLayout | 单行/单列 | 菜单列表、日志输出、设置项 | 子控件高度/宽度不一致时需手动调整Margins |
| GridLayout | 多行多列 | 图标菜单、仪表盘、参数面板 | 子控件数量不能超过行数×列数 |
| ScrollableContainer | 可滚动 | 长列表、大图浏览、日志滚动 | 必须设置滚动方向,内容超出容器尺寸才生效 |
4.3 ScrollableContainer:让内容“动”起来
ScrollableContainer是一个可滚动的容器。你可以把ListLayout或GridLayout放进去,实现列表滚动效果。也可以直接放图片、文本等控件,让用户通过触摸滑动查看超出屏幕的内容。
我曾经做一个工业设备监控界面,参数列表有50多项。屏幕一次只能显示8项。用ScrollableContainer包住ListLayout,再配合setScrollThreshold调整滑动灵敏度,效果非常顺滑。
// 创建一个可滚动的列表
ScrollableContainer scrollContainer;
scrollContainer.setScrollThreshold(15); // 滑动阈值,像素
scrollContainer.setScrollDirection(ScrollableContainer::VERTICAL);
// 把ListLayout放进去
ListLayout innerList;
innerList.setDirection(ListLayout::VERTICAL);
// ... 添加多个子控件到innerList ...
scrollContainer.add(innerList);
4.4 Table与DataGrid:数据展示的“正规军”
Table和DataGrid是TouchGFX里专门用来展示结构化数据的组件。Table更轻量,适合静态数据;DataGrid支持排序、选择、滚动,适合动态数据。
我个人建议:如果数据量不超过100行,用Table就够了。超过100行,或者需要用户交互(比如点击某行查看详情),就用DataGrid。
// 创建一个简单的Table
Table table;
table.setPosition(10, 10, 400, 300);
table.setColWidth(0, 100); // 第0列宽度100
table.setColWidth(1, 150); // 第1列宽度150
table.setColWidth(2, 150); // 第2列宽度150
// 设置表头
table.setHeader(0, "设备ID");
table.setHeader(1, "温度(°C)");
table.setHeader(2, "状态");
// 添加数据行
table.addRow(0, "DEV-001", "36.5", "正常");
table.addRow(1, "DEV-002", "42.1", "告警");
4.5 ProgressBar与Slider:进度与调节
ProgressBar用于显示进度,Slider用于让用户调节数值。这两个控件在TouchGFX里实现起来很直接,但有几个细节要注意。
ProgressBar支持两种模式:PERCENTAGE(百分比)和ABSOLUTE(绝对值)。我一般用百分比模式,配合setValue(0, 100)来设置进度。Slider则支持水平和垂直方向,还可以设置步长。
// ProgressBar示例
ProgressBar progressBar;
progressBar.setXY(20, 20);
progressBar.setWidth(300);
progressBar.setProgressIndicatorPosition(0, 0, 300, 20);
progressBar.setRange(0, 100);
progressBar.setValue(65); // 显示65%
// Slider示例
Slider slider;
slider.setXY(20, 60);
slider.setWidth(300);
slider.setRange(0, 100);
slider.setValue(50);
slider.setStep(5); // 步长5,每次滑动增减5
slider.setNewValueCallback(&onSliderValueChanged);
4.6 自定义数字键盘实现
TouchGFX没有内置的数字键盘组件,但我们可以用Button和TextArea组合实现。这个需求在工业设备、医疗仪器上非常常见——用户需要输入参数,但系统没有物理键盘。
我实现过好几版数字键盘,总结下来核心就三点:
- 布局: 用GridLayout排好0-9、小数点、退格、确认按钮
- 输入逻辑: 每次点击数字按钮,追加到TextArea的文本后面
- 数据校验: 限制小数位数、数值范围、防止多个小数点
// 数字键盘的核心逻辑(伪代码)
class NumericKeyboard : public Container {
public:
NumericKeyboard() {
// 用GridLayout排按钮
grid.setGrid(4, 4); // 4行4列
// 添加按钮:1-9, 0, ., 退格, 确认
for (int i = 0; i < 12; i++) {
Button* btn = new Button();
btn->setBitmaps(Bitmap(KEY_NORMAL), Bitmap(KEY_PRESSED));
btn->setAction(buttonClickedCallback);
grid.add(*btn);
}
}
void buttonClicked(const Button& btn) {
// 获取按钮对应的字符
// 追加到输入框
// 校验数据合法性
}
private:
GridLayout grid;
TextArea inputDisplay;
Unicode::UnicodeChar buffer[16];
};
if (buffer[0] != 0) { 执行退格 }。这种边界情况,测试时很容易漏掉。
4.7 本章知识体系总览
为了让你更直观地理解这些组件之间的关系,我画了一张结构图。它展示了布局容器和高级控件在整个UI框架中的位置,以及它们之间的协作方式。
这张图把本章涉及的组件分成了三大类:布局容器、数据展示组件、交互控件。它们之间通过嵌套和组合,可以搭建出各种复杂的工业级界面。你想想看,一个典型的设备参数设置界面,是不是就是GridLayout排按钮、ScrollableContainer让内容可滚动、再配合数字键盘输入数值?
好了,这一章的内容就到这里。布局容器和高级控件的组合使用,是TouchGFX界面开发的核心技能。多动手试试,你会发现这些组件组合起来能玩出很多花样。