4. UI设计基础:Widgets(控件)体系、Box/Button/TextArea/Image的使用、属性面板详解

好,咱们进入第四讲。前面几章我们把TouchGFX的环境搭好了,也跑通了第一个Demo。但说实话,那个Demo里的界面是人家写好的,你只是点了个编译。从这章开始,我们要亲手往屏幕上“摆”东西了。

在嵌入式GUI里,屏幕上每一个能看见、能交互的元素,都叫Widget,也就是控件。我刚开始接触TouchGFX时,第一反应是:“这不就是画图板吗?” 后来踩了几个坑才发现——它比画图板讲究得多。

核心认知:Widget不是静态图片,它们是“活”的对象。每个Widget都有自己的坐标、大小、颜色、状态,甚至能响应触摸事件。你操作的是C++对象,不是像素。

4.1 Widget体系总览

TouchGFX的Widget继承关系,说白了就是一棵树。根是Drawable,所有能画在屏幕上的东西都从它派生。再往下分两支:容器类和控件类。

TouchGFX Widget 继承体系 Drawable(基类) Container(容器) Widget(控件) Window ModalWindow ScrollableContainer Box Button TextArea Image TextAreaWithWildcard ToggleButton 说明 • 容器类:可以包含子Widget,用于布局分组(如整个页面放一个Window) • 控件类:具体可见元素,每个控件只负责自己的绘制和交互 • 本章重点:Box / Button / TextArea / Image 四个基础控件 • 所有控件都继承自 Drawable,拥有统一的坐标、尺寸、可见性接口

你想想看,为什么要有容器?我刚开始做项目时,把所有Button直接扔在Screen上,结果要整体移动一组控件时,得一个一个改坐标,改到崩溃。后来学乖了——先把控件放进Container,再移动Container,一句话搞定。

4.2 四个最常用的基础控件

咱们一个一个来过。这四个控件,你以后做任何界面都会用到,几乎逃不掉。

4.2.1 Box —— 最纯粹的色块

Box是TouchGFX里最简单的控件。它不显示文字,不显示图片,只显示一个纯色矩形。听起来很鸡肋?其实不然。

  • 用途1:背景色 —— 整个屏幕的背景,用一个Box铺满就行
  • 用途2:分割线 —— 细长的Box当分隔线,比画线方便
  • 用途3:遮罩层 —— 半透明Box盖在弹窗后面,实现“灰掉”效果
  • 用途4:进度条背景 —— 配合另一个Box做填充动画

我的小技巧:Box的alpha通道是可以单独设置的。我在做智能家居面板时,用三个半透明Box叠加,调出了渐变色背景效果——纯代码实现,不用任何图片资源。

4.2.2 Button —— 交互的核心

Button是用户触摸屏幕时最直接的交互对象。TouchGFX的Button有几种风格:

Button类型 特点 适用场景
Button 使用图片,按下/松开两张图切换 自定义外观的按钮
TextButton 显示文字,可设置背景色 纯文字按钮
ToggleButton 按下后保持状态,再按才弹起 开关、锁定/解锁
RepeatButton 按住不放持续触发点击事件 音量加减、数值微调

我记得第一次用Button时,犯了个低级错误——把按钮图片尺寸设得比控件区域小,结果触摸区域比视觉区域大一圈,用户点旁边空白处也能触发。嗯,控件的宽高和图片的宽高是两回事,这个后面属性面板会细说。

4.2.3 TextArea —— 显示文字

TextArea就是用来显示文本的。它有几个关键属性:

  • TypedText —— 指向一个在Texts语言文件中定义的文本ID
  • 颜色 —— 直接设置RGB值
  • 对齐方式 —— 左对齐、居中、右对齐
  • 基线调整 —— 微调文字在控件内的垂直位置

注意:TextArea不能自动换行!如果你需要多行文本,请使用TextAreaWithOneWildcard配合换行符,或者用多个TextArea拼起来。我曾经在显示传感器数据时,发现文字跑到控件外面去了——就是因为没手动换行。

4.2.4 Image —— 显示图片

Image控件用来展示位图。TouchGFX支持多种图片格式,但最终都会转换成内部位图格式。使用Image时要注意:

  1. 图片必须提前导入 —— 通过TouchGFX Designer的Images选项卡导入
  2. 图片尺寸最好和控件一致 —— 否则会拉伸或裁剪
  3. 支持Alpha通道 —— PNG透明背景完全OK
  4. 可以设置缩放算法 —— NEAREST_NEIGHBOR最快,BILINEAR更平滑

性能提醒:Image控件每帧都会重新绘制整张图片。如果你在界面上放了10张大图,帧率会明显下降。我的经验是:能用Box+文字搞定的,就别用图片。尤其是背景,一个Box比一张PNG省几十倍的渲染开销。

4.3 属性面板详解

在TouchGFX Designer里,选中任意一个Widget,右侧就会出现属性面板。很多人觉得这面板没啥好学的,拖拖拽拽就行了。其实不然——属性面板里的每一项,最终都会生成对应的C++代码。你理解了属性,就等于理解了API。

咱们以Button为例,拆解一下属性面板:

属性分组 属性名 说明 我踩过的坑
位置与大小 X / Y 控件左上角相对于父容器的坐标 父容器移动后,子控件坐标不变,但显示位置会变
Width / Height 控件的宽高(像素) Button的宽高决定了触摸响应区域
Visible 是否可见 设为false后不绘制也不响应触摸
外观 Box / Bitmap 选择纯色填充还是图片填充 选Bitmap后要指定图片ID
Alpha 透明度(0-255) 255不透明,0完全透明
交互 Touchable 是否响应触摸事件 设为false后,按钮看起来还在,但点不了
Click Action 点击时触发的回调函数 需要在Screen的setupScreen()里绑定
样式 Style 按钮的按下/松开状态对应的图片 两张图尺寸必须一致,否则会变形

你看,每个属性都不是摆设。我见过有人把Button的Width设成0,结果怎么点都没反应——因为触摸区域是0像素。也见过把Alpha设成0,然后问“为什么按钮不见了?” 嗯,这些坑我都踩过。

4.4 实战:组合使用这四个控件

光说不练假把式。咱们用Box、Button、TextArea、Image做一个简单的“设备控制卡片”:

// 伪代码逻辑,展示控件组合方式
// 1. 创建一个Container作为卡片容器
Container cardContainer;
cardContainer.setPosition(20, 20, 200, 120);

// 2. 添加Box作为卡片背景
Box cardBackground;
cardBackground.setPosition(0, 0, 200, 120);
cardBackground.setColor(Color::getColorFrom24BitRGB(0x2C, 0x3E, 0x50));
cardBackground.setAlpha(200);
cardContainer.add(cardBackground);

// 3. 添加Image显示设备图标
Image deviceIcon;
deviceIcon.setBitmap(Bitmap(BITMAP_FAN_ID));
deviceIcon.setXY(15, 20);
cardContainer.add(deviceIcon);

// 4. 添加TextArea显示设备名称
TextArea deviceName;
deviceName.setTypedText(TypedText(T_DEVICE_NAME));
deviceName.setColor(Color::getColorFrom24BitRGB(0xFF, 0xFF, 0xFF));
deviceName.setXY(60, 25);
cardContainer.add(deviceName);

// 5. 添加Button作为开关
ToggleButton powerButton;
powerButton.setBitmaps(Bitmap(BITMAP_BTN_OFF_ID), Bitmap(BITMAP_BTN_ON_ID));
powerButton.setXY(150, 70);
cardContainer.add(powerButton);

// 6. 将卡片添加到Screen
add(cardContainer);

这段代码虽然简单,但包含了UI开发的核心思路:容器嵌套 + 控件组合 + 坐标布局。你以后做任何复杂的界面,本质上都是在重复这个模式。

我的习惯:在Designer里拖好布局后,我会去生成的代码里看一眼坐标值。有时候Designer里看着对齐了,但代码里X坐标差1个像素——强迫症表示不能忍,直接改代码微调。

4.5 避坑指南

最后,分享几个我这些年积累的经验:

  • 控件重叠时,后添加的在上层 —— 如果想调整层级,用insert()方法指定位置
  • TextArea的文字颜色在Designer里看着和实际不一样 —— 因为Designer的背景色和实际设备不同,建议在目标设备上验证
  • Button的图片资源不要太大 —— 一张200x200的按钮图片,在低端MCU上解码就要好几毫秒
  • Box的setColor()用的是24位RGB —— 别传32位值,否则颜色会偏

我曾经在一个项目里,为了追求视觉效果,用了大量半透明Box叠加。结果在STM32F429上跑,帧率直接掉到15fps。后来把所有半透明层合并成一张预渲染图片,帧率回到了55fps。所以——漂亮和流畅之间,你得学会取舍。

好了,这一章的内容就到这儿。Widget体系是TouchGFX的基石,把这四个控件玩熟了,后面的ListView、Graph、CustomWidget学起来会轻松很多。去打开Designer,亲手拖几个控件试试吧。