4. UI基础组件(上):Box、TextArea、Button、Image的使用与属性配置

好,咱们正式开始动手搭界面了。说实话,TouchGFX里组件不少,但真正天天打交道的,就是今天要讲的这四个:Box、TextArea、Button、Image。你想想看,一个嵌入式设备的屏幕,无非就是背景、文字、按钮、图标这四样东西。把这四个玩透了,后面那些花哨的容器、滑动列表、图表,学起来就快得多。

我个人习惯,每接触一个新组件,先看它“能干什么”,再看它“属性怎么调”,最后看“实际项目里怎么用”。咱们今天就按这个路子走。

核心认知:这四个组件是TouchGFX的“原子级”基础。Box负责填充和背景,TextArea负责文字输出,Button负责交互响应,Image负责图形展示。它们之间没有继承关系,但可以组合使用。

4.1 Box(矩形框)—— 最朴素的填充组件

Box说白了就是一个纯色矩形。没有边框,没有圆角,没有渐变,就是一块颜色。你可能觉得这玩意儿太简单了,但我在项目里发现,很多人恰恰因为觉得它简单,反而用错了地方。

典型用途:

  • 屏幕背景色填充
  • 分割线、状态条
  • 进度条的底色块
  • 遮罩层(配合透明度)

属性配置要点:

属性 说明 我的建议
Color 填充颜色,24位RGB 尽量用调色板里的命名色,别手写十六进制
Alpha 透明度,0-255 做遮罩时常用128左右,全透明用0
Position & Size 位置和尺寸 建议用百分比布局,别写死像素

小技巧:Box的Alpha属性是独立于整个屏幕的透明度的。我曾经做一个夜间模式切换,就是用两个Box叠在一起,上层Box的Alpha从0渐变到180,效果非常平滑,而且不耗额外内存。

嗯,这里要注意一点:Box没有点击事件。它就是个“哑巴”组件。如果你想做一个可点击的矩形区域,请用Button,或者用Box+手势区域组合。我见过有人试图给Box加回调函数,折腾半天发现不行——方向错了。

4.2 TextArea(文本区域)—— 显示文字的正确姿势

TextArea是TouchGFX里最常用的文字组件。你可能会问,为什么不直接用Button显示文字?因为TextArea专门为文字渲染做了优化,支持换行、对齐、字体切换,而且不产生任何交互开销。

核心属性:

  • TypedText: 指向一个预先定义好的文本资源(在Texts文件夹里配)
  • Wildcard: 通配符,用于动态显示数字、变量值
  • Alignment: 左对齐、居中、右对齐
  • Lines: 最大行数,超出部分截断

避坑指南:我曾经在项目里犯过一个低级错误——直接在TextArea的构造函数里写中文。结果编译通过,但屏幕上一片空白。为什么?因为TouchGFX的文本渲染依赖字体文件,你必须先在Assets/Fonts里生成包含中文字符的字体,然后在Texts里定义对应的TypedText。直接写字符串是没用的。

动态更新文本的代码示例:

// 在视图类中更新TextArea内容
// 假设有一个TextArea名为 textAreaValue
// 和一个通配符缓冲区 wildcardBuffer

Unicode::snprintf(wildcardBuffer, BUFFER_SIZE, "%d", sensorValue);
textAreaValue.setWildcard(wildcardBuffer);
textAreaValue.invalidate();  // 强制重绘

这段代码我几乎每个项目都会写。注意invalidate()这行,很多人忘了调,结果数据变了但界面没刷新。说白了,TouchGFX的渲染机制是“脏矩形”模式——你告诉它哪里变了,它才重绘哪里。

4.3 Button(按钮)—— 交互的起点

Button是用户和系统交互的桥梁。TouchGFX提供了好几种按钮变体,但最常用的是Button(图片按钮)和TextButton(文字按钮)。

按钮类型对比:

类型 触发方式 状态图 适用场景
Button 点击释放时触发 按下/释放两张图片 图标按钮、自定义样式
TextButton 同上 按下/释放两种文字样式 菜单项、表单按钮
ToggleButton 点击切换状态 选中/未选中两张图片 开关、模式切换
RepeatButton 按住持续触发 同Button 音量加减、数值微调

我个人最常用的是Button,因为它最灵活。你给它两张图——一张正常态,一张按下态——它就能工作。但要注意图片尺寸必须一致,否则会拉伸变形。我刚开始做的时候就被这个坑过,按下去按钮突然变大了,用户体验极差。

绑定回调函数:

// 在视图的setupScreen()中绑定
buttonConfirm.setAction(buttonConfirmHandler);

// 回调函数实现
void MyView::buttonConfirmHandler(const AbstractButton& src) {
    // 处理点击事件
    if (&src == &buttonConfirm) {
        // 执行确认逻辑
        executeConfirm();
    }
}

经验之谈:回调函数里不要做耗时操作。我曾经在一个按钮回调里直接写Flash写入操作,结果界面卡死了一秒多。正确的做法是:在回调里设置一个标志位,然后在tick()函数里处理耗时任务。这样界面始终流畅。

4.4 Image(图片)—— 让界面活起来

Image组件就是用来显示图片的。它支持PNG、JPG、BMP等格式,但实际项目中我几乎只用PNG——因为支持透明通道,而且TouchGFX对PNG的渲染效率最高。

图片资源管理:

  • 所有图片必须导入到Assets/Images文件夹
  • TouchGFX Designer会自动生成对应的BitmapId
  • 图片尺寸最好是2的幂次(如64x64、128x128),但不是强制

动态切换图片:

// 切换图片显示
imageStatus.setBitmap(Bitmap(BITMAP_ICON_WIFI_ON_ID));
imageStatus.invalidate();

// 或者用缩放模式
imageStatus.setScalingMode(Image::FIT_ZOOM);  // 保持比例缩放

嗯,这里有个细节:setScalingMode有几种模式,FIT_ZOOM是保持比例缩放,FILL是拉伸填充,NONE是原尺寸。我建议图标类用NONE,背景图用FILL,产品图用FIT_ZOOM。你想想看,一个圆形的Logo被拉伸成扁的,多难看。

内存警告:TouchGFX的图片是直接解码到内存的。一张800x480的24位PNG,解压后占用800*480*3 ≈ 1.1MB内存。如果你的MCU只有512KB RAM,那就得用L8格式(8位索引色)或者RGB565(16位色)来压缩。我曾在资源紧张的项目里把所有图片转成L8格式,内存占用直接砍半。

4.5 组件组合实战:一个简单的状态卡片

光讲理论没意思,咱们来组合一下。假设我们要做一个设备状态卡片,包含背景、标题、状态图标、操作按钮。用今天学的四个组件就能搞定。

布局思路:

  1. Box作为卡片背景,白色半透明,圆角效果用图片模拟
  2. TextArea显示“设备状态”标题
  3. Image显示在线/离线图标
  4. Button作为“刷新”按钮

你看,没有用到任何高级容器,四个基础组件就搭出了一个可用的UI模块。实际项目中,这种组合方式占了界面元素的80%以上。

核心原则:能用基础组件解决的问题,就不要引入复杂容器。基础组件渲染开销低、内存占用小、调试方便。我见过有人为了一个圆角效果,硬是用了Container+Box+Painter,其实一张圆角PNG图片就解决了。

UI基础组件(上)知识体系 四大基础组件 Box 矩形框 TextArea 文本 Button 按钮 Image 图片 Box 属性 Color / Alpha Position & Size TextArea 属性 TypedText / Wildcard Alignment / Lines Button 类型 Button / TextButton Toggle / Repeat Image 要点 BitmapId / 格式 缩放模式 / 内存 组合使用 → 状态卡片、菜单项、仪表盘 基础组件覆盖 80% 的界面需求

这张图把今天的内容串起来了。你看,四个组件各有专长,但最终都要组合使用。我建议你学完每个组件后,都试着把它们组合成一个小的UI片段。别急着上复杂动画,先把静态界面搭稳了。地基不牢,后面加动画只会越改越乱。