自訂小部件
在創建應用時,您可能需要TouchGFX中未包含的小部件。 TouchGFX提供了幾種可以用來創建圖形元素的方式。 最簡單的方式是使用自訂容器法,您可以將現有的小部件組合成您自己的小部件。 但是,本文將介紹一種更好的方法,您可以用它真正地創建一個可全面控制影像緩衝的小部件,從而能夠精確地繪製您想要的內容。
自訂小部件法的使用場合
創建自訂小部件不是創建小部件的最典型方法。 如果符合您的需求,最好選擇自訂容器法,但有時此法並不夠用。 當您需要全面控制影像緩衝時,您需要使用自訂小部件法。
下面是幾個可以使用自訂小部件法創建的小部件的範例:
- 褐色濾鏡
- 曼德勃羅分形小部件
- 顯示檔中資料的小部件,如gif動畫。
TouchGFX Designer中
TouchGFX Designer目前不支援自訂小部件的創建。 因此,您將需要手動寫入自訂小部件的程式碼(請參考本文的剩餘部分以瞭解做法),然後在視圖的使用者程式碼部分插入小部件。
程式碼中
通過擴展小部件
類創建自己的自訂小部件。 這樣做會加大用戶的工作量,但也可以全面控制小部件繪製的所有像素。 您的自訂小部件不一定要利用任何現有小部件,而是可以通過指定像素顏色來定義應如何繪製。 自訂小部件法的存儲空間占用量通常較小,這在某些情況下非常重要。
自訂小部件的一個範例是二維碼小部件。 這個特殊的小部件是個很好的例子,我們將在下面一節中詳細介紹如何創建它。 在本例中,二維碼小部件是黑白像素的NxN矩陣。 小部件的大小和每個像素的顏色將在執行時間確定,並取決於二維碼資料物件中的資訊。
下面是兩個二維碼小部件可能外觀的範例:
Caution
Tip
touchgfx::Button
(隨框架提供)作為自訂小部件,而不是自訂容器。 這樣可以節省螢幕上每個按鈕的存儲空間。您自己的自訂小部件
為了創建擴展小部件
類的小部件,您需要宣告兩點。
- 小部件的繪製方式
- 小部件的實心部分
部分繪製
任何小部件和自訂小部件都需要支援部分繪製。 這意味著小部件應能只繪製自身的一部分。
其中的原因有兩點。 通常不一定要重新繪製整個螢幕,而是只需繪製一部分。 TouchGFX的演算法可以將全屏繪製分割成多個部分繪製,以使繪製像素的總數最小化。 然後,螢幕的部分繪製可能要求小部件只繪製自身的一部分。
例如,二維碼小部件需能夠支持只繪製自身的highlighted 部分。
在程式碼中實現這一點的方法是重寫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
是小部件中需要更新的那部分。 在我們的二維碼範例中,無效區域是highlighted 區。 該區域表示為相對于小部件左上角的座標。
Caution
Tip
方法
是 const
,因為最優繪製演算法可以將小部件的繪製分割成多個呼叫進行繪製。 繪製 const
確保小部件在多次繪製執行之間不發生 方法
更新等。實心區域
此外,小部件需能夠報告自身多大一部分是實心的。 實心意味著您不能在螢幕上看到它後方的內容。 例如,標準紅色方塊是完全實心的,而一幅alpha 值為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::DISPLAY_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版本。
- 其次,存在破壞依賴于特定行為的標準小部件和容器的風險。
因此,如果必須修改標準小部件/容器,我們建議您複製它、重新命名然後使用,而不是直接修改原始程式碼。
也就是說,您可以自由地做任何您認為對的事情。 如果將標準小部件的原始程式碼添加到專案中,連結器會選擇此版本而不是核心庫中的版本。 因此,通過將原始程式碼添加到應用,可以修改標準小部件。