自定义控件
在创建应用时,您可能需要TouchGFX中没有包含的控件。 TouchGFX提供了几种可以用来创建图形元素的方式。 最简单的方式是使用自定义容器法,您可以将现有的控件组合成您自己的控件。 但是,本文将介绍一种更高级的方法,您可以用它真正地创建一个可全面控制帧缓冲的控件,从而能够精确地绘制您想要的内容。
自定义控件法的使用场景
创建自定义控件不是创建控件的最典型方法。 如果适合您的需求,最好选择自定义容器法,但有时此法并不够用。 当您需要全面控制帧缓冲时,您需要使用自定义控件法。
下面是几个可以且应当使用自定义控件法创建的控件的示例:
- 褐色滤镜
- 曼德勃罗分形控件
- 显示文件中数据的控件,如gif动画。
在TouchGFX Designer中
TouchGFX Designer目前不支持自定义控件的创建。 因此,您将需要手动写入自定义控件的代码(请参考本文的剩余部分了解做法),然后在视图的用户代码部分插入控件。
在代码中
通过扩展Widget
类创建自己的自定义控件。 这样做会加大用户的工作量,但也可以全面控制控件绘制的所有像素。 您的自定义控件不一定要利用任何现有控件,而是可以通过指定像素颜色来定义应如何绘制。 自定义控件法的存储空间占用量通常较小,这在某些情况下非常重要。
自定义控件的一个示例是二维码控件。 这个特殊的控件是个很好的例子,我们将在下面一节中详细介绍如何创建它。 在本例中,二维码控件是黑白像素的NxN矩阵。 控件的大小和每个像素的颜色将在运行时间确定,并取决于二维码数据对象中的信息。
下面是两个二维码控件可能外观的示例:
Caution
Tip
touchgfx::Button
(随框架提供)作为自定义控件,而不是自定义容器。 这样可以节省屏幕上每个按钮的存储空间。您自己的自定义控件
为了创建扩展Widget
类的控件,您需要声明两点。
- 控件的绘制方式
- 控件的实心部分
部分绘制
任何控件和自定义控件都需要支持部分绘制。 这意味着控件应能只绘制自身的一部分。
其中的原因有两点。 通常不一定要重新绘制整个屏幕,而是只需绘制一部分。 TouchGFX的算法可以将全屏绘制分割成多个部分绘制,以使绘制像素的总数最小化。 然后,屏幕的部分绘制可能要求控件只绘制自身的一部分。
例如,二维码控件需能够支持只绘制自身的高亮部分。
在代码中实现这一点的方法是重写draw
方法:
virtual void draw(const touchgfx::Rect& invalidatedArea) const
{
//run through the pixels of the invalidated area
//draw a black pixel if the qr data object has a value at this position
//draw a white pixel otherwise
}
invalidatedArea
是控件中需要更新的那部分。 在我们的二维码示例中,无效区域是高亮区域。 该区域表示为相对于控件左上角的坐标。
Caution
Tip
方法
是 const,
因为最优绘制算法可以将控件的绘制分割成多个调用进行绘制。 绘制 const,
确保控件在多次绘制执行之间不发生移动、 方法
等。实心区域
此外,控件需能够报告自身多大一部分是实心的。 实心意味着您不能在屏幕上看到它后方的内容。 例如,标准红色方块是完全实心的,而一幅阿尔法值为50%的图像则完全非实心;您可以看到它后方的一切。
实心控件能使TouchGFX加快绘制进度。 由于我们无需在实心控件下方绘制控件,因此要绘制的像素较少。
为了报告代码中的实心区域,重写getSolidRect
方法。
virtual Rect getSolidRect() const;
得到的二维码将是完全实心的。 您将不能看到黑白像素后的任何内容。 因此,我们让该方法返回一个矩形,其大小等于控件的完整尺寸:
virtual Rect getSolidRect() const
{
return touchgfx::Rect(0, 0, getWidth(), getHeight());
}
示例源代码
我们最终得到一个应用和一个这样的控件 - 具体取决于提供的数据:
控件的完整代码如下:
gui/include/gui/common/QRCodeWidget.hpp
#ifndef QR_CODE_WIDGET_HPP
#define QR_CODE_WIDGET_HPP
#include <touchgfx/widgets/Widget.hpp>
class QRCodeWidget : public touchgfx::Widget
{
public:
QRCodeWidget();
virtual void draw(const touchgfx::Rect& invalidatedArea) const;
virtual touchgfx::Rect getSolidRect() const;
void setQRCodeData(QRCodeData* data);
void setScale(uint8_t s);
private:
void updateSize();
QRCodeData* data;
uint8_t scale;
};
#endif
在头文件中,将控件定义为touchgfx::Widget
类的扩展。 重写draw
和getSolidRect
方法,以便定义如何绘制控件。 声明设置二维码数据和设置二维码比例因子的方法。
gui/src/common/QRCodeWidget.cpp
#include <gui/common/QRCodeWidget.hpp>
QRCodeWidget::QRCodeWidget() :
data(0),
scale(1)
{
}
void QRCodeWidget::draw(const touchgfx::Rect& invalidatedArea) const
{
if (!data)
{
return;
}
touchgfx::Rect absolute = getAbsoluteRect();
uint16_t* framebuffer = touchgfx::HAL::getInstance()->lockFrameBuffer();
for (int y = invalidatedArea.y; y < invalidatedArea.bottom(); y++)
{
for (int x = invalidatedArea.x; x < invalidatedArea.right(); x++)
{
framebuffer[absolute.x + x + (absolute.y + y) * touchgfx::HAL::FRAME_BUFFER_WIDTH] =
data->at(x / scale, y / scale) ? 0x0000 : 0xffff;
}
}
touchgfx::HAL::getInstance()->unlockFrameBuffer();
}
touchgfx::Rect QRCodeWidget::getSolidRect() const
{
return touchgfx::Rect(0, 0, getWidth(), getHeight());
}
void QRCodeWidget::setQRCodeData(QRCodeData* qrCode)
{
data = qrCode;
updateSize();
}
void QRCodeWidget::setScale(uint8_t s)
{
scale = s;
updateSize();
}
void QRCodeWidget::updateSize()
{
if (data)
{
setWidth(data->getSize() * scale);
setHeight(data->getSize() * scale);
}
}
源文件定义了draw
方法。 此方法遍历无效区域中的每个像素,并基于数据对象的内容和比例因子确定帧缓冲的颜色。
getSolidRect
方法报告控件为完全实心。
Caution
方法
方法中。 这样做的目的是在我们开始绘制时确保DMA完成了绘制。代码还使用小类/接口访问二维码数据:
class QRCodeData
{
public:
uint8_t getSize();
bool at(uint8_t x, uint8_t y);
};
Things to try
修改标准控件/容器
在安装TouchGFX时,包含了标准控件的源代码。 为了适应应用需求,这些也都可以进行修改。 标准控件和容器的源代码位于以下文件夹:
Middlewares\ST\touchgfx\framework\source\touchgfx\widgets
为了维持向后兼容,核心库包含标准控件和容器的已编译版本。 因此,并非必须在项目中编译这些文件。
Caution
源代码主要用作灵感来源,以及一种学习TouchGFX控件内部工作机制的方式。 如需一些不同于标准实现的行为,可通过子类化或创建自定义容器来实现,这也是推荐方法。
推荐原因有两个:
- 首先,由于必须手动合并所做的任何修改,因此会更加难以升级到更新的TouchGFX版本。
- 其次,存在破坏依赖于特定行为的标准控件和容器的风险。
因此,如果必须修改标准控件/容器,我们建议您复制它、重命名然后使用,而不是直接修改源代码。
也就是说,您可以自由地做任何您认为对的事情。 如果将标准控件的源代码添加到项目中,链接器会选择此版本而不是核心库中的版本。 因此,通过将源代码添加到应用,可以修改标准控件。