跳轉到主要內容

自訂小部件

在創建應用時,您可能需要TouchGFX中未包含的小部件。 TouchGFX提供了幾種可以用來創建圖形元素的方式。 最簡單的方式是使用自訂容器法,您可以將現有的小部件組合成您自己的小部件。 但是,本文將介紹一種更好的方法,您可以用它真正地創建一個可全面控制影像緩衝的小部件,從而能夠精確地繪製您想要的內容。

自訂小部件法的使用場合

創建自訂小部件不是創建小部件的最典型方法。 如果符合您的需求,最好選擇自訂容器法,但有時此法並不夠用。 當您需要全面控制影像緩衝時,您需要使用自訂小部件法。

下面是幾個可以使用自訂小部件法創建的小部件的範例:

  • 褐色濾鏡
  • 曼德勃羅分形小部件
  • 顯示檔中資料的小部件,如gif動畫。

TouchGFX Designer中

TouchGFX Designer目前不支援自訂小部件的創建。 因此,您將需要手動寫入自訂小部件的程式碼(請參考本文的剩餘部分以瞭解做法),然後在視圖的使用者程式碼部分插入小部件。

程式碼中

通過擴展小部件類創建自己的自訂小部件。 這樣做會加大用戶的工作量,但也可以全面控制小部件繪製的所有像素。 您的自訂小部件不一定要利用任何現有小部件,而是可以通過指定像素顏色來定義應如何繪製。 自訂小部件法的存儲空間占用量通常較小,這在某些情況下非常重要。

自訂小部件的一個範例是二維碼小部件。 這個特殊的小部件是個很好的例子,我們將在下面一節中詳細介紹如何創建它。 在本例中,二維碼小部件是黑白像素的NxN矩陣。 小部件的大小和每個像素的顏色將在執行時間確定,並取決於二維碼資料物件中的資訊。

下面是兩個二維碼小部件可能外觀的範例:

二維碼小部件範例

Caution
可以用自訂容器法創建二維碼小部件,使容器有n*n個黑或白方塊作為子容器。 但是,這會佔用許多存儲空間,並且很可能不夠高效或靈活。
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類的擴展。 重寫drawgetSolidRect方法,以便定義如何繪製小部件。 宣告設置二維碼資料和設置二維碼比例因數的方法。

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);
};
Further reading
下載並查看QRCodeLens小部件。
Things to try
  • 修改二維碼小部件,使白色像素完全透明。
  • 創建顯示褐色濾鏡、曼德勃羅分形、gif圖像或其他內容的自訂小部件。
  • 思考使用自訂容器最容易實現哪些小部件,以及使用自訂小部件法最容易實現哪些小部件。
  • 修改標準小部件/容器

    在安裝TouchGFX時,包含了標準小部件的原始程式碼。 為了適應應用需求,這些也都可以進行修改。 標準小部件和容器的原始程式碼位於以下資料夾:

    Middlewares\ST\touchgfx\framework\source\touchgfx\widgets

    為了維持向後相容,核心庫包含標準小部件和容器的已編譯版本。 因此,並非必須在專案中編譯這些檔。

    Caution
    不建議直接修改標準小部件/容器

    原始程式碼主要用作靈感來源,以及一種學習TouchGFX小部件內部工作機制的方式。 如需一些不同於標準實現的行為,可通過子類化或創建自訂容器來實現,這也是推薦方法。

    推薦原因有兩個:

    • 首先,由於必須手動合併所做的任何修改,因此會更加難以升級到更新的TouchGFX版本。
    • 其次,存在破壞依賴于特定行為的標準小部件和容器的風險。

    因此,如果必須修改標準小部件/容器,我們建議您複製它、重新命名然後使用,而不是直接修改原始程式碼。

    也就是說,您可以自由地做任何您認為對的事情。 如果將標準小部件的原始程式碼添加到專案中,連結器會選擇此版本而不是核心庫中的版本。 因此,通過將原始程式碼添加到應用,可以修改標準小部件。