容器是TouchGFX中的一种组件,可以包含子节点(比如控件和其他容器)。
在TouchGFX Designer中,可以在Widgets(控件)选项卡中的containers(容器)类别下找到容器,向容器中添加控件的方法将控件拖放到树视图中的容器中。
子容器在Z轴上的顺序由向容器中添加子容器时的顺序决定 - 最后添加的子容器将显示在屏幕的最前面。
由于TouchGFX中控件的位置是相对于其父容器而确定的,因此改变父容器的位置也会相应地移动子容器。
容器充当视窗,这意味着只有与容器几何图形相交的子容器部分才可见。
在下面的动画中,您将看到容器的视窗部分是如何工作的。 首先,我们看到这个按钮的父容器的轮廓:
代码
可以看出可以向容器中添加子节点,形成新的控件
#ifndef TOUCHGFX_CONTAINER_HPP
#define TOUCHGFX_CONTAINER_HPP
#include <touchgfx/Callback.hpp>
#include <touchgfx/Drawable.hpp>
#include <touchgfx/hal/Types.hpp>
namespace touchgfx
{
/* 容器 */
class Container : public Drawable
{
public:
Container() : Drawable(), firstChild(0)
{
}
//添加一个Drawable实例作为此容器的子项。
//添加的Drawable将被放置在最后绘制,因此会出现在容器之前添加的所有Drawable的顶部
virtual void add(Drawable& d);
//从容器中移除一个Drawable,通过将其从子项链表中移除。
//如果Drawable不在子项列表中,则不会发生任何事情。
virtual void remove(Drawable& d);
//从容器中移除所有Drawable
virtual void removeAll();
//通过取消链接第一个子项来移除所有子项。子项的父项和同级指针不会被重置。
virtual void unlink();
//查询是否已将给定的Drawable直接添加到此容器中。搜索不是递归进行的。
virtual bool contains(const Drawable& d);
//在特定的子节点之后插入一个Drawable。如果上一个子节点为0,则将Drawable插入为列表的第一个元素。
virtual void insert(Drawable* previous, Drawable& d);
//获取子项列表中的最后一个子项。
//如果此容器是可触摸的,它将作为结果返回。
//否则,将递归遍历所有可见的子项,以找到与给定坐标相交的Drawable。
virtual void getLastChild(int16_t x, int16_t y, Drawable** last);
// 与getLastChild()类似,但还会考虑HAL中当前设置的手指大小。
virtual void getLastChildNear(int16_t x, int16_t y, Drawable** last, int16_t* fingerAdjustmentX, int16_t* fingerAdjustmentY);
/* 绘制该图。仅绘制包含在矩形内部分 */
virtual void draw(const Rect& invalidatedArea) const;
/* 获取可以保证为实心的(不透明的)最大矩形 */
virtual Rect getSolidRect() const;
//对容器中的每个子项执行指定的回调函数。要执行的回调函数必须具有以下原型:void T::func(Drawable&)
virtual void forEachChild(GenericCallback<Drawable&>* function);
//获取此容器的第一个子项的指针。
//第一个子项是首先绘制的Drawable,因此它位于此容器的所有其他子项的后面。
virtual Drawable *getFirstChild()
{
return firstChild;
}
protected:
// 获取一个矩形,该矩形描述此容器的子项所覆盖的总区域。
virtual Rect getContainedArea() const;
// 对所有子项调用moveRelative(移动到的相对位置)。
virtual void moveChildrenRelative(int16_t deltaX, int16_t deltaY);
Drawable *firstChild; //指向此容器的第一个子项的指针。后续子项可以通过firstChild的nextSibling找到。
friend class Screen;
//设置绘制链,对给定的无效区域进行绘制,并准备下一个要绘制的元素。
virtual void setupDrawChain(const Rect& invalidatedArea, Drawable** nextPreviousElement);
};
}
#endif
#include <touchgfx/containers/Container.hpp>
#include <touchgfx/hal/HAL.hpp>
namespace touchgfx
{
//查询是否已将给定的Drawable直接添加到此容器中。搜索不是递归进行的。
bool Container::contains(const Drawable& d)
{
bool found = false;
Drawable *head = firstChild;
while(head && !found)
{
found = (head == &d);
head = head->nextSibling;
}
return found;
}
//添加一个Drawable实例作为此容器的子项,尾插
void Container::add(Drawable& d)
{
assert(&d != this && "Cannot add Drawable to self");
assert(d.parent == 0 && "Cannot add Drawable multiple times");
//初始化该相的父对象和下一个对象
d.parent = this;
d.nextSibling = 0;
//容器子项列表为空
if(!firstChild)
{
firstChild = &d;
}
/* 不为空,尾插 */
else
{
Drawable *head = firstChild;
while(head->nextSibling)
{
assert(head != &d && "Cannot add Drawable multiple times");
head = head->nextSibling;
}
assert(head != &d && "Cannot add Drawable multiple times");
head->nextSibling = &d;
}
}
//从容器中移除一个Drawable,通过将其从子项链表中移除。
void Container::remove(Drawable& d)
{
if(!firstChild)
{
return;
}
/* 是第一个节点 */
if(&d == firstChild)
{
d.parent = 0;
if(!d.nextSibling)
{
firstChild = 0;
}
else
{
firstChild = d.nextSibling;
d.nextSibling = 0;
}
return;
}
/* 不是第一个节点 */
Drawable *tmp = firstChild;
while(tmp)
{
if (tmp->nextSibling == &d)
{
tmp->nextSibling = d.nextSibling;
d.parent = 0;
d.nextSibling = 0;
return;
}
else
{
tmp = tmp->nextSibling;
}
}
}
//从容器中移除所有Drawable
void Container::removeAll()
{
while (firstChild)
{
Drawable* d = firstChild;
firstChild = firstChild->nextSibling;
d->parent = 0;
d->nextSibling = 0;
}
}
//通过取消链接第一个子项来移除所有子项。子项的父项和同级指针不会被重置。
void Container::unlink()
{
firstChild = 0;
}
/* 绘制所有子项。仅绘制包含在矩形内部分 */
void Container::draw(const Rect& invalidatedArea) const
{
if(!isVisible() || !firstChild)
{
return;
}
/* 循环绘制所有子项 */
const Rect tmp = invalidatedArea;
Drawable *d = firstChild;
while (d)
{
/* 控件可见 */
if(d->isVisible())
{
Rect drawableRegion = tmp & d->getRect();
if(!drawableRegion.isEmpty())
{
/* drawableRegion在d中的坐标 */
drawableRegion.x -= d->getX();
drawableRegion.y -= d->getY();
/* 绘制d */
d->draw(drawableRegion);
}
}
d = d->nextSibling;
}
}
//获取该坐标相交的容器最顶层子项
void Container::getLastChild(int16_t x, int16_t y, Drawable **last)
{
/* 容器可触摸,则容器算作最底层 */
if(isTouchable())
{
*last = this;
}
/* 循环获取和坐标相交最顶层子项 */
Drawable *d = firstChild;
while (d)
{
/* d可见且坐标位于d内 */
if(d->isVisible() && d->getRect().intersect(x, y))
{
const int16_t xadj = x - d->getX();
const int16_t yadj = y - d->getY();
d->getLastChild(xadj, yadj, last);
}
d = d->nextSibling;
}
}
// 与getLastChild()类似,但还会考虑HAL中当前设置的手指大小。
void Container::getLastChildNear(int16_t x, int16_t y, Drawable **last, int16_t *fingerAdjustmentX, int16_t *fingerAdjustmentY)
{
/* 获取手指大小 */
const int fingerSize = HAL::getInstance()->getFingerSize();
*fingerAdjustmentX = 0;
*fingerAdjustmentY = 0;
*last = 0;
//获取该坐标相交的容器最顶层子项
Container::getLastChild(x, y, last);
const int fingerSizeDistance = 3; // Up to this number is not multi-sampled
if(fingerSize > fingerSizeDistance)
{
const Rect meAbsRect = getAbsoluteRect();
uint32_t bestDistance = 0xFFFFFFFF;
Drawable *previous = 0; // Speed up calculations if we hit the same drawable on next sample
if(*last)
{
// Touched a drawable, but perhaps there is a better alternative
previous = *last;
const Rect absRect = (*last)->getAbsoluteRect();
const int dx = (x + meAbsRect.x) - (absRect.x + (absRect.width / 2));
const int dy = (y + meAbsRect.y) - (absRect.y + (absRect.height / 2));
bestDistance = dx * dx + dy * dy;
}
const int samplePoints[2][4][2] = { { { 0, -1 }, { -1, 0 }, { 1, 0 }, { 0, 1 } }, // above, left, right, below
{ { -1, -1 }, { 1, -1 }, { -1, 1 }, { 1, 1 } } }; // up-left, up-right, down-left and down-right
const int maxRings = 3;
const int numRings = MIN(maxRings, (fingerSize - 1) / fingerSizeDistance);
for (int ring = 0; ring < numRings; ring++)
{
// For each 'ring' "distance" increases up to "fingerSize":
const int distance = fingerSize * (ring + 1) / numRings;
for (int sampleIndex = 0; sampleIndex < 4; sampleIndex++)
{
const int* xy = samplePoints[ring % 2][sampleIndex];
const int16_t deltaX = xy[0] * distance;
const int16_t deltaY = xy[1] * distance;
if (rect.intersect(x + deltaX, y + deltaY))
{
Drawable* drawable = 0;
Container::getLastChild(x + deltaX, y + deltaY, &drawable);
if (drawable && drawable != previous)
{
previous = drawable;
const Rect absRect = drawable->getAbsoluteRect();
// Find distance to center of drawable
const int dx = (x + meAbsRect.x) - (absRect.x + (absRect.width / 2));
const int dy = (y + meAbsRect.y) - (absRect.y + (absRect.height / 2));
const uint32_t dist = dx * dx + dy * dy;
// Check if this drawable center is closer than the previous
if (dist < bestDistance)
{
bestDistance = dist;
*last = drawable;
*fingerAdjustmentX = deltaX;
*fingerAdjustmentY = deltaY;
}
}
}
}
}
}
}
/* 获取可以保证为实心的(不透明的)最大矩形 */
Rect Container::getSolidRect() const
{
return Rect();
}
// 获取一个矩形,该矩形描述此容器的子项所覆盖的总区域。
Rect Container::getContainedArea() const
{
Drawable* d = firstChild;
Rect contained;
while (d)
{
contained.expandToFit(d->getRect());
d = d->nextSibling;
}
return contained;
}
// 对所有子项调用moveRelative(移动到的相对位置)。
void Container::moveChildrenRelative(int16_t deltaX, int16_t deltaY)
{
Drawable* d = firstChild;
while (d)
{
d->moveRelative(deltaX, deltaY);
d = d->nextSibling;
}
}
//对容器中的每个子项执行指定的回调函数。要执行的回调函数必须具有以下原型:void T::func(Drawable&)
void Container::forEachChild(GenericCallback<Drawable&>* function)
{
Drawable* d = firstChild;
while (d)
{
function->execute(*d);
d = d->nextSibling;
}
}
//在特定的子节点之后插入一个Drawable。如果上一个子节点为0,则将Drawable插入为列表的第一个元素。
void Container::insert(Drawable* previous, Drawable& d)
{
if (!firstChild)
{
// Insert as only element
add(d);
return;
}
else if (!previous)
{
// Insert as head element
d.nextSibling = firstChild;
firstChild = &d;
d.parent = this;
}
else
{
Drawable* tmp = firstChild;
while (tmp)
{
if (tmp == previous)
{
d.nextSibling = tmp->nextSibling;
tmp->nextSibling = &d;
d.parent = this;
return;
}
tmp = tmp->nextSibling;
}
}
}
//设置绘制链,对给定的无效区域进行绘制,并准备下一个要绘制的元素。
void Container::setupDrawChain(const Rect& invalidatedArea, Drawable** nextPreviousElement)
{
// This function adds the children of this container to the list of drawables to draw.
if (!isVisible())
{
// If this container itself is not visible, do not add anyone to draw chain.
return;
}
if (!firstChild)
{
// If this container is empty, do not add anyone.
return;
}
Drawable* d = firstChild;
while (d)
{
if (d->isVisible())
{
// Only drawables intersecting with the specified invalidated area will be added.
Rect drawableRegion = invalidatedArea & d->getRect();
if (!drawableRegion.isEmpty())
{
drawableRegion.x -= d->getX();
drawableRegion.y -= d->getY();
d->setupDrawChain(drawableRegion, nextPreviousElement);
}
}
d = d->nextSibling;
}
}
}
touchgfx提供的几种容器
1.Container
容器是TouchGFX中的组件,可包含子节点。
容器位于TouchGFX Designer中的容器控件组中
TouchGFX Designer中容器的属性
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
位置 | X 和Y 指定控件左上角相对于其父的位置。 W 和 H 指定控件的宽度和高度。 锁定指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见 指定控件的可见性。 使控件不可见还将禁用与控件之间通过屏幕进行的交互。 |
缓存 | 指定容器是否应生成为CachableContainer形式。 |
容器支持的操作
控件特有的操作 | 说明 |
---|---|
调整控件的尺寸 | 调整控件的宽度和高度。 |
标准控件操作 | 说明 |
---|---|
移动控件 | 随时间的推移将控件移动到新位置。 |
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
2.ScrollableContainer
可滚动容器属于容器,允许垂直和水平滚动其内容。
可滚动容器位于TouchGFX Designer中的容器控件组中。
TouchGFX Designer中可滚动容器的属性
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
位置 | X 和Y 指定控件左上角相对于其父的位置。 W 和 H 指定控件的宽度和高度。 锁定指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见 指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
滚动 | 启用水平滚动指定是否启用水平滚动。 启用垂直滚动指定是否启用垂直滚动。 显示滚动条指定滚动条是否应始终可见。 滚动时显示滚动条指定滚动条是否仅应在内容进行滚动时可见。 如果“显示滚动条”已启用,则将忽略此选项。 滚动条颜色 指定滚动条的颜色。 滚动条Alpha 指定滚动条的透明度。 控件Alpha值的范围是0到255。 0表示完全透明,255表示不透明。 |
可滚动容器支持的操作
控件特有的操作 | 说明 |
---|---|
调整控件的尺寸 | 调整控件的宽度和高度。 |
标准控件操作 | 说明 |
---|---|
移动控件 | 随时间的推移将控件移动到新位置。 |
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
3.SwipeContainer
TouchGFX中的滑动容器是由多个页构成专门化的容器,可在各页之间滑动进行访问。 滑动容器中的页可包含其他控件,与容器类似。
滑动容器位于TouchGFX Designer中的容器控件组中
下面的部分介绍了滑动容器的属性
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
位置 | X 和Y 指定控件左上角相对于其父的位置。 W 和 H 指定控件的宽度和高度。 锁定指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见 指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
页 | 已选页 指定画布上显示的页面。 该页也将作为项目运行时的起始页。 点击+按钮时会创建新页。 |
页指示符 | 显示页指示符指定页指示符的可见性。 X和Y指定页指示符左上角相对于控件左上角的位置。 水平居中指定页指示符的位置是否应在控件的x轴上居中。 样式指定控件的预定义设置,用于将所选属性设为预定义的值。 这些样式包含可免费使用的图像。. 正常图像和高亮图像指定分配给PageIndicator的正常和高亮状态的图像。 |
滑动设置 | 滑动阈值指定切换页前用户需要滑动的距离。 结束滑动弹性宽度指定可将第一页和最后一页滑动到控件边界以外而不停止的距离。 |
下面的部分介绍了滑动容器支持的操作
控件特有的操作 | 说明 |
---|---|
调整控件的尺寸 | 调整控件的宽度和高度。 |
标准控件操作 | 说明 |
---|---|
移动控件 | 随时间的推移将控件移动到新位置。 |
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
4.ListLayout
列表布局控件属于容器,会自动按给定方向将其子控件排列在列表中。 向列表布局添加控件或从列表布局中移除控件会重新排列子部件。
列表布局位于TouchGFX Designer中的容器控件组中。
TouchGFX Designer中列表布局的属性。
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
位置 | X 和 Y 指定控件左上角相对于其父的位置。 W 和 H 指定控件的宽度和高度。 列表布局的大小总计为其子部件的总大小。 锁定 指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见 指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
方向 | 方向指定布局的排列方向。 在沿东向(向右)排列的水平布局与沿西向(向下)排列的垂直布局之间进行选择。 |
TouchGFX Designer中的列表布局支持的操作
标准控件操作 | 说明 |
---|---|
移动控件 | 随时间的推移将控件移动到新位置。 |
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
5.ModalWindow
模式窗口是容器类型的控件,用于显示窗口,并禁止对下层视图和控件的触摸事件。 模式窗口由背景图像和边框构成,边框充当覆盖下层视图和控件的阴影,其Alpha值可调整。 模式窗口将填充整个屏幕,并始终应作为最后一个元素添加,以使其始终位于其他所有元素的顶部。
模式窗口位于TouchGFX Designer中的容器控件组中。
TouchGFX Designer中模式窗口的属性。
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
模式图像位置 | X 和 Y指定图像左上角在模式窗口中的位置。 W 和 H 指定容器在模式窗口中的宽度和高度。 容器在模式窗口中的大小是从关联图像的大小获取的,其大小无法更改(除非更改图像)。 锁定 指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见 指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
外观 | 窗口图像 指定模式窗口应使用的图像。 阴影颜色 指定叠加阴影的颜色。 Shade Alpha 指定叠加阴影的透明度。 控件Alpha值的范围是0到255。 0表示完全透明,255表示不透明。 |
下面的部分介绍了模式窗口支持的操作
标准控件操作 | 说明 |
---|---|
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
6.ScrollList
滚动列表是可滚动的菜单,由若干项目和控件构成,这些项目和控件滚动到视图中时会进行动态更新。 与滚动列表中的项目交互时,滚动列表还能调用回调函数。
滚动列表位于TouchGFX Designer中的容器控件组中。
TouchGFX Designer中滚动列表的属性。
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
类型 | 类型指定滚动列表方向为垂直方向或水平方向 |
位置 | X 和Y 指定控件左上角相对于其父的位置。 W 和 H 指定控件的宽度和高度。 锁定指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见 指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
项目模板 | 项目模板指定用作模板的CustomContainer。 选项数指定滚动列表中存在的项目数。 |
列表外观 | 循环指定到达列表末尾后,滚动列表中的项目是否将循环。 项目对齐指定项目是否应该对齐。 如果对齐为False,则项目可随意移动。 如果对齐为True,项目将按位对齐,始终位于所选位置。 项目间距指定项目之间的间距。 填充前 和 填充后 指定滚动列表中可见画板前后的距离偏移量。 |
动画 | 缓动和缓动选项指定动画使用的缓动方程。 Swipe Acc. 和 Drag Acc. 指定滚动时的加速度。 |
滚动列表中的项目基于名为“项目模板”的概念,项目模板属于CustomContainer,用作滚动列表中各项目图形元素的基础。 创建滚动列表之前,应创建自定义容器,为滚动列表提供项目模板。
创建滚动列表后,可在项目模板属性下选择CustomContainer。 指定项目模板会调整滚动列表大小,以适应所选自定义容器不在可滚动方向上的的尺寸属性(垂直滚动列表的宽度、水平滚动列表的高度)。 更改其他尺寸属性(垂直滚动列表的高度、水平滚动列表的宽度)决定了可见的项目数。
下面的部分介绍了滚动列表支持的操作
标准控件操作 | 说明 |
---|---|
移动控件 | 随时间的推移将控件移动到新位置。 |
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
7.ScrollWheel
滚轮是包含多个项目的可滚动菜单,滚动浏览菜单中的项目时,这些项目会动态更新,且被选中的项目将突出显示。 启用响应与滚轮交互的代码后,可基于与轮中项目的交互调用不同的回调函数。
滚轮位于TouchGFX Designer中的容器控件组中。
TouchGFX Designer中滚轮的属性。
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
类型 | 类型指定滚轮方向为垂直方向或水平方向。 |
位置 | X 和Y 指定控件左上角相对于其父的位置。 W 和 H 指定控件的宽度和高度。 锁定指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见 指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
项目模板 | 项目模板 指定用作模板的CustomContainer。 选项数 指定滚动列表中存在的项目数。 首选项目 指定首先选择哪个项目。 使用已选样式模板 指定是否为所选项目使用单独的模板。 已选样式模板 指定用作选定模板的CustomContainer。 |
列表外观 | 循环 指定到达列表末尾后,滚轮中的项目是否将循环。 选中项偏移量 指定选定项目的位置。 项目边距 指定项目之间的间距。 前方额外尺寸和后方额外尺寸 指定显示已选样式模板的区域的大小。 前边距和后边距指定显示已选样式模板的区域前后的边距大小。 |
动画 | 缓动和缓动选项指定动画使用的缓动方程。 Swipe Acc. 和 Drag Acc. 指定滚动时的加速度。 |
滚动列表中的项目基于名为“项目模板”的概念,项目模板属于CustomContainer,用作滚轮中各项目图形元素的基础。 为了突出显示所选项目,滚轮包含用于选择名为“已选样式模板”的项目模板的选项,已选样式模板仅可用于已选项目。 创建滚轮之前,应为项目模板以及已选项目模板(若启用)创建CustomContainer。
创建滚轮后,可在项目模板属性下选择CustomContainer。 为项目模板选择自定义容器时,会调整滚轮大小,以适应所选自定义容器不在可滚动方向上的的尺寸属性(垂直方向的宽度、水平方向的高度)。 更改其他尺寸属性(垂直方向的高度、水平方向的宽度)决定了可见的项目数。
下面的部分介绍了滚轮支持的操作
标准控件操作 | 说明 |
---|---|
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
8.SlideMenu
TouchGFX中的滑动菜单是由内部容器、图像和可选按钮构成的专门化的容器,可以动画形式显示收起和展开状态的变化过程。
滑动菜单位于TouchGFX Designer中的容器控件组中。
下面的部分介绍了滑动菜单的属性。
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
位置 | X 和Y 指定控件左上角相对于其父的位置。 W 和 H 指定控件的宽度和高度。 滑动菜单的大小是由其背景和按钮图像的大小决定的。 锁定指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见 指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
展开方向 | 展开方向指定滑动菜单的展开方向。 |
状态 | 状态指定滑动菜单的初始状态。 收起:可见像素数指定状态为收起时应可见的像素数。 展开:隐藏像素数 指定状态为展开时应隐藏的像素数。 展开超时指定从展开状态自动恢复到收起状态之前的时长。 |
背景 | 背景图像 指定用作背景的图像。 背景位置 指定背景图像相对于控件坐标的x、y坐标。 |
按钮 | 使用按钮 指定是否使用按钮来控制控件的状态。 释放图像指定用于按钮已发布状态的图像。 按下图像指定用于按钮已按下状态的图像。 按钮位置指定按钮相对于控件坐标的x、y坐标。 |
动画 | 缓动和缓动选项指定动画使用的缓动方程。 持续时间指定动画应持续的时长。 |
下面的部分介绍了滑动菜单支持的操作
控件特有的操作 | 说明 |
---|---|
更改滑动菜单的状态 | 将滑动菜单的状态改为收起或展开 |
复位滑动菜单的定时器 | 复位定时器会自动将滑动菜单状态恢复为收起状态 |
标准控件操作 | 说明 |
---|---|
移动控件 | 随时间的推移将控件移动到新位置。 |
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
下面的部分介绍了滑动菜单支持的触发条件
触发条件 | 说明 |
---|---|
滑动菜单动画结束 | 从一种状态变为另一种状态的滑动菜单动画已结束。 |
滑动菜单状态已更改 | 滑动菜单的状态已更改。 |
9.Slider
滑块使用三幅图像在垂直或水平方向显示滑块。 可以拖动滑块的指示图像修改通过回调函数广播的内部整数值。 广播的值位于一个整数值范围(例如0-100)内。
滑块位于TouchGFX Designer中的Miscellaneous控件组中。
TouchGFX Designer中滑块的属性。
属性组 | 属性说明 |
---|---|
名称 | 控件的名称。 名称是TouchGFX Designer和代码中使用的唯一标识符。 |
类型 | 类型指定滑块应该是垂直方向还是水平方向。 |
位置 | X和Y指定控件左上角相对于其父的位置。 W和H指定控件的宽度和高度。 滑块的大小是从关联图像的大小获取的,其大小无法更改(除非更改图像)。 锁定指定控件是否应锁定为其当前的X、Y、W和H。 如果锁定控件,还会禁止通过屏幕与控件进行交互。 可见指定控件的可见性。 如果将控件标记为不可见,还会禁止通过屏幕与控件进行交互。 |
样式 | 样式指定控件的预定义设置,用于将所选属性设为预定义的值。 这些样式包含可免费使用的图像。 |
图像 | 背景图像指定指示器滑过的背景图像。 背景填充图像指定填充指示器后面背景图像顶部区域的图像。 指示图像指定可拖动以更改滑块值的图像。 背景图像和背景填充图像的大小必须一致。 |
位置 | X轴背景位置和Y轴背景位置指定背景图像和背景填充图像的左上角位置。 指示器位置最小值和指示器位置最大值指定指示器图像位置的最小值和最大值。 对于水平滑块,这两个值在x轴上;对于垂直滑块,这两个值在y轴上。 Y轴指示器位置指定指示器图像在Y轴上的位置。 如果滑块是垂直移动的,则该值应在X轴上进行调整。 |
值 | 最小值和最大值指定使用回调函数从滑块进行广播的内部整数值范围。 初始指定滑块中的初始内部值。 这同样会改变指示器的初始位置。 |
下面的部分介绍了滑块支持的操作
标准控件动作 | 说明 |
---|---|
移动控件 | 随时间的推移将控件移动到新位置。 |
隐藏控件 | 隐藏控件(将可见性设置为false)。 |
显示控件 | 使隐藏的控件可见(将可见性设置为true)。 |
下面的部分介绍了滑块支持的触发条件
触发 | 说明 |
---|---|
启动滑块调整 | 滑块已被点击或拖动。 |
滑块调整已确认 | 滑块指示器不再被拖动。 |
滑块值已更改 | 滑块值已更改。 |
10.Keyboard
代码
#ifndef TOUCHGFX_KEYBOARD_HPP
#define TOUCHGFX_KEYBOARD_HPP
#include <touchgfx/Callback.hpp>
#include <touchgfx/TypedText.hpp>
#include <touchgfx/Unicode.hpp>
#include <touchgfx/containers/Container.hpp>
#include <touchgfx/events/ClickEvent.hpp>
#include <touchgfx/events/DragEvent.hpp>
#include <touchgfx/hal/Types.hpp>
#include <touchgfx/widgets/Image.hpp>
#include <touchgfx/widgets/TextAreaWithWildcard.hpp>
namespace touchgfx
{
class Keyboard : public Container
{
public:
/* 单个按键结构体 */
struct Key
{
uint8_t keyId; //按键ID
Rect keyArea; //按键区域
BitmapId highlightBitmapId; //按键按下时位图ID
};
/* 矩形区域回调 */
struct CallbackArea
{
Rect keyArea; //按键区域
GenericCallback<>* callback; //当区域被“按下”时要执行的回调
BitmapId highlightBitmapId; //按键按下时位图ID
};
/* 键盘布局的定义。键盘可以处理不断变化的布局,因此可以通过改变布局和键映射来实现不同的键盘模式 */
struct Layout
{
BitmapId bitmap; //键盘位图ID
const Key *keyArray; //按键数组
uint8_t numberOfKeys; //按键个数
CallbackArea *callbackAreaArray; //区域回调数组
uint8_t numberOfCallbackAreas; //区域回调个数
Rect textAreaPosition; //键盘缓冲区显示区域
TypedText textAreaFont; //键盘缓冲区显示区域TypedText
colortype textAreaFontColor; //键盘缓冲区显示区域TypedText颜色
FontId keyFont; //按键字体
colortype keyFontColor; //按键字体颜色
};
/* 按键字符映射 */
struct KeyMapping
{
uint8_t keyId; //按键ID
Unicode::UnicodeChar keyValue; //按键字符
};
/* 按键字符映射表 */
struct KeyMappingList
{
const KeyMapping *keyMappingArray; //按键字符映射数组指针
uint8_t numberOfKeys; //按键字符映射数组大小
};
Keyboard();
/* 设置键盘要使用的缓冲区。输入的按键将添加到缓冲区 */
void setBuffer(Unicode::UnicodeChar *newBuffer, uint16_t newBufferSize);
/* 设置键盘布局 */
void setLayout(const Layout *newLayout);
/* 设置缩进 */
void setTextIndentation();
/* 获取键盘布局 */
const Layout *getLayout() const
{
return layout;
}
/* 设置按键字符映射表 */
void setKeymappingList(const KeyMappingList *newKeyMappingList);
/* 获取按键字符映射表 */
const KeyMappingList *getKeyMappingList() const
{
return keyMappingList;
}
/* 设置输入缓冲区当前指针 */
void setBufferPosition(uint16_t newPos);
/* 过去输入缓冲区当前指针 */
uint16_t getBufferPosition()
{
return bufferPosition;
}
/* 获取输入缓冲区指针 */
Unicode::UnicodeChar *getBuffer() const
{
return buffer;
}
/* 绘制该图(绘制所有按键字符)。仅绘制包含在矩形内部分 */
virtual void draw(const Rect& invalidatedArea) const;
/* 点击事件 */
virtual void handleClickEvent(const ClickEvent& event);
/* 拖拽事件:按下且离开按键区域 */
virtual void handleDragEvent(const DragEvent& event);
/* 设置按键释放时回调 */
void setKeyListener(GenericCallback<Unicode::UnicodeChar>& callback)
{
keyListener = &callback;
}
protected:
GenericCallback<Unicode::UnicodeChar> *keyListener; //按键释放时回调指针
Unicode::UnicodeChar *buffer; //输入缓冲区
uint16_t bufferSize; //输入缓冲区长度
uint16_t bufferPosition; //输入缓冲区当前指针
Image image; //布局image
TextAreaWithOneWildcard enteredText; //能够显示输入的文本缓冲区
const Layout *layout; //键盘布局的定义
const KeyMappingList *keyMappingList; //按键字符映射表
Image highlightImage; //按键高亮时显示位图
bool cancelIsEmitted; //按键事件取消(按下后离开控件区域)
/* 根据坐标获取该按键 */
Key getKeyForCoordinates(int16_t x, int16_t y) const;
/* 获取按键字符 */
Unicode::UnicodeChar getCharForKey(uint8_t keyId) const;
/* 根据坐标获取该区域回调 */
CallbackArea getCallbackAreaForCoordinates(int16_t x, int16_t y) const;
/// @cond
/**
* Add to draw chain.
*
* @param invalidatedArea Include drawables that intersect with this area only.
* @param [in,out] nextPreviousElement Modifiable element in linked list.
*
* @note For TouchGFX internal use only.
*/
virtual void setupDrawChain(const Rect& invalidatedArea, Drawable** nextPreviousElement);
};
}
#endif
#include <touchgfx/Bitmap.hpp>
#include <touchgfx/Color.hpp>
#include <touchgfx/Drawable.hpp>
#include <touchgfx/Font.hpp>
#include <touchgfx/FontManager.hpp>
#include <touchgfx/hal/HAL.hpp>
#include <touchgfx/lcd/LCD.hpp>
#include <touchgfx/widgets/Keyboard.hpp>
namespace touchgfx
{
Keyboard::Keyboard()
: Container(), keyListener(0), buffer(0), bufferSize(0), bufferPosition(0), image(), enteredText(), layout(0), keyMappingList(0), highlightImage(), cancelIsEmitted(false)
{
setTouchable(true);
image.setXY(0, 0);
Keyboard::add(image);
highlightImage.setVisible(false);
Keyboard::add(highlightImage);
enteredText.setColor(Color::getColorFromRGB(0, 0, 0));
Keyboard::add(enteredText);
}
/* 设置键盘输入的文本缓冲区 */
void Keyboard::setBuffer(Unicode::UnicodeChar *newBuffer, uint16_t newBufferSize)
{
buffer = newBuffer;
bufferSize = newBufferSize;
enteredText.setWildcard(buffer);
bufferPosition = Unicode::strlen(buffer);
}
/* 设置键盘布局 */
void Keyboard::setLayout(const Layout *newLayout)
{
layout = newLayout;
if(newLayout != 0)
{
/* 设置新的布局位图 */
image.setBitmap(Bitmap(newLayout->bitmap));
/* 设置缓冲区显示的显示的字体、颜色、区域 */
enteredText.setTypedText(newLayout->textAreaFont);
enteredText.setColor(newLayout->textAreaFontColor);
enteredText.setPosition(newLayout->textAreaPosition.x, newLayout->textAreaPosition.y,
newLayout->textAreaPosition.width, newLayout->textAreaPosition.height);
}
/* 重绘 */
invalidate();
}
/* 设置缩进 */
void Keyboard::setTextIndentation()
{
if (layout != 0)
{
const uint8_t indentation = layout->textAreaFont.getFont()->getMaxPixelsLeft();
enteredText.setPosition(layout->textAreaPosition.x - indentation, layout->textAreaPosition.y,
layout->textAreaPosition.width + indentation * 2, layout->textAreaPosition.height);
enteredText.setIndentation(indentation);
}
}
/* 根据坐标获取该按键 */
Keyboard::Key Keyboard::getKeyForCoordinates(int16_t x, int16_t y) const
{
Key key;
key.keyId = 0; // No key
if (layout != 0)
{
for (uint8_t i = 0; i < layout->numberOfKeys; i++)
{
if (layout->keyArray[i].keyArea.intersect(x, y))
{
key = layout->keyArray[i];
break;
}
}
}
return key;
}
/* 根据坐标获取该区域回调 */
Keyboard::CallbackArea Keyboard::getCallbackAreaForCoordinates(int16_t x, int16_t y) const
{
CallbackArea area;
area.callback = reinterpret_cast<GenericCallback<>*>(0);
if(layout != 0)
{
for (uint8_t i = 0; i < layout->numberOfCallbackAreas; i++)
{
if (layout->callbackAreaArray[i].keyArea.intersect(x, y))
{
area = layout->callbackAreaArray[i];
break;
}
}
}
return area;
}
/* 绘制该图。仅绘制包含在矩形内部分 */
void Keyboard::draw(const Rect& invalidatedArea) const
{
assert(layout && "No layout configured for Keyboard");
/* 绘制所有按键字符 */
if(layout != 0)
{
Font *font = FontManager::getFont(layout->keyFont);
assert(font && "Keyboard::draw: Unable to find font, is font db initialized?");
if (font != 0)
{
// Setup visuals for h-center of "string"
LCD::StringVisuals visuals;
visuals.font = font;
visuals.alignment = CENTER;
visuals.color = layout->keyFontColor;
// String with room for a single character
Unicode::UnicodeChar character[2] = { 0, 0 }; // The last is important as string terminator.
const uint16_t fontHeight = font->getHeight();
for (uint8_t i = 0; i < layout->numberOfKeys; i++)
{
const Key& key = layout->keyArray[i];
if (key.keyArea.intersect(invalidatedArea))
{
const uint8_t keyId = key.keyId;
const Unicode::UnicodeChar c = getCharForKey(keyId);
if (c != 0)
{
// Get a copy of the keyArea and v-center the area for the character
Rect keyArea = key.keyArea;
const uint16_t offset = (keyArea.height - fontHeight) / 2;
keyArea.y += offset;
keyArea.height -= offset;
// Calculate the invalidated area relative to the key
Rect invalidatedAreaRelative = key.keyArea & invalidatedArea;
invalidatedAreaRelative.x -= keyArea.x;
invalidatedAreaRelative.y -= keyArea.y;
// Set up string with one character
character[0] = c;
translateRectToAbsolute(keyArea);
HAL::lcd().drawString(keyArea, invalidatedAreaRelative, visuals, character);
}
}
}
}
}
}
/* 点击事件 */
void Keyboard::handleClickEvent(const ClickEvent& event)
{
const ClickEvent::ClickEventType type = event.getType();
/* 不检查按键松开事件:手离开后区域后松开不处理 */
if(type == ClickEvent::RELEASED && cancelIsEmitted)
{
cancelIsEmitted = false;
return;
}
const int16_t x = event.getX();
const int16_t y = event.getY();
Rect toDraw;
/* 根据坐标获取该区域回调 */
Keyboard::CallbackArea callbackArea = getCallbackAreaForCoordinates(x, y);
/* 有回调:功能按键 */
if(callbackArea.callback != 0)
{
/* 按下重绘 */
if(type == ClickEvent::PRESSED)
{
/* 绘制按键高亮 */
highlightImage.setXY(callbackArea.keyArea.x, callbackArea.keyArea.y);
highlightImage.setBitmap(Bitmap(callbackArea.highlightBitmapId));
highlightImage.setVisible(true);
toDraw = highlightImage.getRect();
invalidateRect(toDraw);
}
/* 松开按键,回调 */
if((type == ClickEvent::RELEASED) && callbackArea.callback->isValid())
{
callbackArea.callback->execute();
if(keyListener)
{
keyListener->execute(0);
}
}
}
/* 没有回调:字符按键 */
else
{
/* 根据坐标获取该按键 */
const Keyboard::Key key = getKeyForCoordinates(x, y);
/* 按下 */
if (type == ClickEvent::PRESSED)
{
/* 绘制按键高亮 */
if(key.keyId != 0)
{
highlightImage.setXY(key.keyArea.x, key.keyArea.y);
highlightImage.setBitmap(Bitmap(key.highlightBitmapId));
highlightImage.setVisible(true);
toDraw = highlightImage.getRect();
invalidateRect(toDraw);
}
}
/* 松开按键,重绘键盘缓冲区显示区域 */
if (type == ClickEvent::RELEASED)
{
if (key.keyId != 0 && buffer)
{
const Unicode::UnicodeChar c = getCharForKey(key.keyId);
if (c != 0 && bufferPosition < (bufferSize - 1))
{
enteredText.invalidateContent();
buffer[bufferPosition++] = c;
buffer[bufferPosition] = 0;
enteredText.invalidateContent();
if (keyListener)
{
keyListener->execute(c);
}
}
}
}
}
/* 按键释放/取消,不显示高亮图片 */
if (type == ClickEvent::RELEASED || type == ClickEvent::CANCEL)
{
toDraw = highlightImage.getRect();
highlightImage.setVisible(false);
invalidateRect(toDraw);
if(type == ClickEvent::CANCEL)
{
cancelIsEmitted = true;
}
}
}
/* 拖拽事件:按下且离开按键区域 */
void Keyboard::handleDragEvent(const DragEvent& event)
{
if(highlightImage.isVisible() && (!highlightImage.getRect().intersect(static_cast<int16_t>(event.getNewX()), static_cast<int16_t>(event.getNewY()))) && (!cancelIsEmitted))
{
//按键取消事件
const ClickEvent cancelEvent(ClickEvent::CANCEL, static_cast<int16_t>(event.getOldX()), static_cast<int16_t>(event.getOldY()));
handleClickEvent(cancelEvent);
}
}
/* 获取按键字符 */
Unicode::UnicodeChar Keyboard::getCharForKey(uint8_t keyId) const
{
Unicode::UnicodeChar ch = 0;
if (keyMappingList != 0)
{
for (uint8_t i = 0; i < keyMappingList->numberOfKeys; i++)
{
if (keyMappingList->keyMappingArray[i].keyId == keyId)
{
ch = keyMappingList->keyMappingArray[i].keyValue;
break;
}
}
}
return ch;
}
/* 将绘图插入容器的绘图链 */
void Keyboard::setupDrawChain(const Rect& invalidatedArea, Drawable **nextPreviousElement)
{
/* 将该容器中所有绘图插入父容器的绘图链 */
Container::setupDrawChain(invalidatedArea, nextPreviousElement);
/* 将该容器自己插入父容器的绘图链 */
Drawable::setupDrawChain(invalidatedArea, nextPreviousElement);
}
/* 设置输入缓冲区当前指针 */
void Keyboard::setBufferPosition(uint16_t newPos)
{
bufferPosition = newPos;
enteredText.invalidate();
}
/* 设置按键字符映射表 */
void Keyboard::setKeymappingList(const KeyMappingList *newKeyMappingList)
{
keyMappingList = newKeyMappingList;
invalidate();
}
}