跳轉到主要內容

教程3:多個螢幕的應用

在本教程中,您將學習如何在一個應用中建立多個螢幕,以及在兩個螢幕之間共用資料。 我們將建立一個模擬時鐘的應用,它將用一個螢幕來設定小時和分鐘,並將時間傳遞到另一個具有運作中時鐘的螢幕。

您還將學習如何使用TouchGFX Designer根據互動 (如螢幕切換) 建立動畫。

本教程使用的圖像可從此連結下載。 將檔案解壓縮到硬碟上的目錄。

第1步:設定兩個螢幕

如教程2中所做,建立一個新專案。 同樣地,圖形是以480x272的解析度進行設計。 如果專案是以「STM32F746G探索套件」的應用程式範本為基礎,專案將使用此解析度。 如果您有具有其他解析度的Demo開發板,應能修改圖形以符合您的需求。 如果沒有Demo開發板,則直接以「模擬器」應用程式範本為基礎,且務必輸入480x272作為解析度。

設定Screen1

要建立的第一個螢幕是Screen1,可以在該螢幕上設定小時和分鐘值,然後發送到具有運作中時鐘的螢幕。 Screen1顯示如下,包含兩個文字方塊,其中包含時鐘所需的時間。 使用向上和向下箭頭調整文字方塊中的值。 使用對應文字方塊下方的「儲存」按鈕儲存所選值,並將它們傳遞到時鐘。 最後,按下「時鐘」按鈕切換到顯示時鐘的Screen2。

我們首先為應用插入背景和文字區域。 插入小工具並更新屬性,如下列圖片和表格所示。

screen1的螢幕截圖,其中背景位置和文字區域突出顯示

位置小工具屬性
1圖像
  • 名稱:background
  • 位置:
    • X:0、Y:0
  • 圖像:background_Screen1.png
2TextArea
  • 名稱:textAreaHourCaption
  • 位置:
    • X:86、Y:46
    • W:85、H:24
  • 文字:
    • 文字:小時
    • 字體排印:20px
    • 對齊:置中
  • 外觀:
    • 顏色:#FFABABAB
3TextArea
  • 名稱:textAreaHour
  • 位置:
    • X:87、Y:70
    • W:83、H:50
  • 文字:
    • 文字: <value>
    • 萬用字元1:
      • 使用萬用字元緩衝區:是
      • 初始值:00
      • 緩衝區大小:3
    • 字體排印:40px
    • 對齊:置中
  • 外觀:
    • 顏色:#FFABABAB
4TextArea
  • 名稱:textAreaMinuteCaption
  • 位置:
    • X:309、Y:46
    • W:85、H:24
  • 文字:
    • 文字:分鐘
    • 字體排印:20px
    • 對齊:置中
  • 外觀:
    • 顏色:#FFABABAB
5TextArea
  • 名稱:textAreaMinute
  • 位置:
    • X:311、Y:70
    • W:83、H:50
  • 文字:
    • 文字: <value>
    • 萬用字元1:
      • 使用萬用字元緩衝區:是
      • 初始值:00
      • 緩衝區大小:3
    • 字體排印:40px
    • 對齊:置中
  • 外觀:
    • 顏色:#FFABABAB

然後,依下面的圖片和表格所示,將按鈕插入應用。

screen1的螢幕截圖,其中按鈕位置突出顯示

位置小工具屬性
1按鈕
  • 名稱:buttonHourUp
  • 位置:
    • X:184、Y:51
  • 圖像:
    • 已釋放圖像:Up_arrow.png
    • 已按下圖像:Up_arrow_pressed.png
2按鈕
  • 名稱:buttonHourDown
  • 位置:
    • X:184、Y:93
  • 圖像:
    • 已釋放圖像:Down_arrow.png
    • 已按下圖像:Down_arrow_pressed.png
3按鈕
  • 名稱:buttonMinuteUp
  • 位置:
    • X:266、Y:51
  • 圖像:
    • 已釋放圖像:Up_arrow.png
    • 已按下圖像:Up_arrow_pressed.png
4按鈕
  • 名稱:buttonMinuteDown
  • 位置:
    • X:266、Y:93
  • 圖像:
    • 已釋放圖像:Down_arrow.png
    • 已按下圖像:Down_arrow_pressed.png
5含標籤的按鈕
  • 名稱:buttonSaveHour
  • 位置:
    • X:80、Y:137
  • 文字:
    • 文字:儲存
    • 字體排印:25px
    • 對齊:置中
  • 文字外觀:
    • 釋放顏色:#FF424242
    • 按下顏色:#FFA6A6A6
  • 圖像:
    • 已釋放圖像:btn_round.png
    • 已按下圖像:btn_round_pressed.png
6含標籤的按鈕
  • 名稱:buttonSaveMinute
  • 位置:
    • X:303、Y:137
  • 文字:
    • 文字:儲存
    • 字體排印:25px
    • 對齊:置中
  • 文字外觀:
    • 釋放顏色:#FF424242
    • 按下顏色:#FFA6A6A6
  • 圖像:
    • 已釋放圖像:btn_round.png
    • 已按下圖像:btn_round_pressed.png
7含標籤的按鈕
  • 名稱:buttonClock
  • 位置:
    • X:192、Y:204
  • 文字:
    • 文字:時鐘
    • 字體排印:25px
    • 對齊:置中
  • 文字外觀:
    • 釋放顏色:#FF424242
    • 按下顏色:#FFA6A6A6
  • 圖像:
    • 已釋放圖像:btn_round.png
    • 已按下圖像:btn_round_pressed.png

在設定了圖形元素後,下一步是為按鈕新增觸發條件,它會調整所選值並儲存:

互動屬性
點擊「小時值增加」按鈕
  • 觸發條件:點擊按鈕
  • 點擊來源:buttonHourUp
  • 動作:呼叫新的虛擬函數
  • 函數名稱:buttonHourUpClicked
點擊「小時值減少」按鈕
  • 觸發條件:點擊按鈕
  • 點擊來源:buttonHourDown
  • 動作:呼叫新的虛擬函數
  • 函數名稱:buttonHourDownClicked
點擊「分鐘值增加」按鈕
  • 觸發條件:點擊按鈕
  • 點擊來源:buttonMinuteUp
  • 動作:呼叫新的虛擬函數
  • 函數名稱:buttonMinuteUpClicked
點擊「分鐘值減少」按鈕
  • 觸發條件:點擊按鈕
  • 點擊來源:buttonMinuteDown
  • 動作:呼叫新的虛擬函數
  • 函數名稱:buttonMinuteDownClicked
點擊「儲存小時值」按鈕
  • 觸發條件:點擊按鈕
  • 點擊來源:buttonSaveHour
  • 動作:呼叫新的虛擬函數
  • 函數名稱:buttonSaveHourClicked
點擊「儲存分鐘值」按鈕
  • 觸發條件:點擊按鈕
  • 點擊來源:buttonSaveMinute
  • 動作:呼叫新的虛擬函數
  • 函數名稱:buttonSaveMinuteClicked

如果您按下「產生程式碼」或「執行模擬器」按鈕,TouchGFX Designer將產生指定的虛擬函數。 我們首先整合箭頭按鈕的四個函數。 為了記錄小時和分鐘值,還需新增兩個計數器。

現在新增下列程式碼:

TouchGFX/gui/include/gui/screen1_screen/Screen1View.hpp
...
public:
...
virtual void buttonHourUpClicked();
virtual void buttonHourDownClicked();
virtual void buttonMinuteUpClicked();
virtual void buttonMinuteDownClicked();

protected:
int16_t hour;
int16_t minute;
...

在按下箭頭按鈕時,對應的值會發生變化,以便符合時鐘值的範圍。

應依以下方式增加四個函數的邏輯:

TouchGFX/gui/src/screen1_screen/Screen1View.cpp
Screen1View::Screen1View()
: hour(0), minute(0) //clear the new counters
{
}
...
void Screen1View::buttonHourUpClicked()
{
hour = (hour + 1) % 24; // Keep new value in range 0..23
Unicode::snprintf(textAreaHourBuffer, TEXTAREAHOUR_SIZE, "%02d", hour);
textAreaHour.invalidate();
}

void Screen1View::buttonHourDownClicked()
{
hour = (hour + 24 - 1) % 24; // Keep new value in range 0..23
Unicode::snprintf(textAreaHourBuffer, TEXTAREAHOUR_SIZE, "%02d", hour);
textAreaHour.invalidate();
}

void Screen1View::buttonMinuteUpClicked()
{
minute = (minute + 1) % 60; // Keep new value in range 0..59
Unicode::snprintf(textAreaMinuteBuffer, TEXTAREAMINUTE_SIZE, "%02d", minute);
textAreaMinute.invalidate();
}

void Screen1View::buttonMinuteDownClicked()
{
minute = (minute + 60 - 1) % 60; // Keep new value in range 0..59
Unicode::snprintf(textAreaMinuteBuffer, TEXTAREAMINUTE_SIZE, "%02d", minute);
textAreaMinute.invalidate();
}

設定Screen2

第二個螢幕Screen2是顯示運作時鐘的螢幕,從Screen1中儲存的值開始。 除了時鐘,Screen2還包含一個圍繞時鐘活動的圓,表示時鐘正在運作。 最後,為了返回Screen1,實作一個將螢幕切換到Screen1的按鈕。

用模擬器執行應用時的screen2螢幕截圖

在將元素新增到Screen2之前,必須建立一個新螢幕。 這是透過TouchGFX Designer中的「新增螢幕」按鈕 (1) 完成的,如下圖所示。

1

TouchGFX Designer中「新增螢幕」按鈕的位置

在進入「screen2」時,我們要讓時鐘和圓移動到它們的位置,要從螢幕以外移動到視圖中:時鐘從左側移入,圓從右側移入。 因此,兩個小工具最初會位於TouchGFX Designer的畫布之外。

應按照下面的圖片和表格所示放置小工。

screen2中小工具的位置

位置小工具屬性
1圖像
  • 名稱:background
  • 位置:
    • X:0、Y:0
  • 圖像:background_Screen2.png
1按鈕
  • 名稱:buttonSettings
  • 位置:
    • X:422、Y:10
  • 圖像:
    • 釋放圖像:configuration.png
    • 按下圖像:configuration.png
3TextArea
  • 名稱:buttonClock
  • 位置:
    • X: -124、Y: 109
    • W:124、H:54
  • 文字:
    • 文字:<hour>:<min>
    • 萬用字元1:
      • 初始值:00
      • 使用萬用字元緩衝區:是
      • 緩衝區大小:3
    • 萬用字元2:
      • 初始值:00
      • 使用萬用字元緩衝區:是
      • 緩衝區大小:3
    • 字體排印:40px
    • 對齊:置中
  • 外觀:
    • 顏色:#FFABABAB
4
  • 名稱:circle
  • 位置:
    • X:479、Y:36
    • W:200、H:200
  • 圖像/顏色:
    • 顏色:#FFBABABA
  • 外觀:
    • 中心位置:
      • X:100、Y:100
    • 起始角度和結束角度:
      • 開始:0,結束:180
    • 半徑:72
    • 線條寬度:6
    • 端點樣式:三角形

在新增所有小工具後,螢幕應如下圖所示。 TextArea和Circle位於螢幕之外,因此不可見。

新增到Screen2的所有小工具

在螢幕之間切換

現在,我們要新增功能,以便在兩個螢幕之間切換。 為此,我們為Screen1上的「時鐘」按鈕和Screen2上的「設定」按鈕指派互動。 此外,我們要在進入Screen2時讓位於Screen2上螢幕區之外的兩個元素移動到位。

為此,為Screen1新增下列互動:

互動屬性
切換到「Screen2」
  • 觸發條件:點擊按鈕
  • 點擊來源:buttonClock
  • 動作:切換螢幕
  • 螢幕:Screen2
  • 轉換:封面
  • 轉換方向:北

此外,為Screen2新增下列互動:

互動屬性
切換到「Screen1」
  • 觸發條件:點擊按鈕
  • 點擊來源:buttonSettings
  • 動作:切換螢幕
  • 螢幕:Screen1
  • 轉換:滑動
  • 轉換方向:南
將圓移動到位
  • 觸發條件:螢幕轉換結束
  • 動作:移動小工具
  • 要移動的小工具:circle
  • 位置:140, 36
  • 緩動:立方體,緩出
  • 時長:750ms
將文字時鐘移入到位
  • 觸發條件:螢幕轉換結束
  • 動作:移動小工具
  • 要移動的小工具:textClock
  • 位置:178, 109
  • 緩動:立方體,緩出
  • 時長:750ms

在新增互動後,螢幕如下圖所示:

新增到Screen2的所有小工具

為了在執行階段更新時鐘和使圓移動,我們使用handleTickEvent虛擬函數。

TouchGFX框架定期(每一畫面)呼叫handleTickEvent,能夠動態地更新使用中螢幕裡的元素,在本例中為時鐘和圓。

與Screen1類似,使用小時和分鐘計數器監測時鐘。 由於handleTickEvent 的呼叫頻率高於時鐘的更新頻率,因此增加了tickCounter來確定兩次時鐘更新之間的tick數。 使用addStart和addEnd函數更新圓中的圓弧角度。 將 handleTickEvent函數和變數新增到Screen2View.hpp,如下圖所示。

TouchGFX/gui/include/gui/screen2_screen/Screen2View.hpp
public:
...
virtual void handleTickEvent();

protected:
int16_t hour;
int16_t minute;
int16_t tickCount;
int16_t addStart;
int16_t addEnd;
...

Screen2View.cpp中實作handleTickEvent,因此可更新時鐘和圓的程式碼如下所示

TouchGFX/gui/src/screen2_screen/Screen2View.cpp
Screen2View::Screen2View()
: hour(0), minute(0), tickCount(0), addStart(1), addEnd(2)
{ }
...
void Screen2View::handleTickEvent()
{
if (tickCount == 60)
{
minute++;
hour = (hour + (minute / 60)) % 24;
minute %= 60;

Unicode::snprintf(textClockBuffer1, TEXTCLOCKBUFFER1_SIZE, "%02d", hour);
Unicode::snprintf(textClockBuffer2, TEXTCLOCKBUFFER2_SIZE, "%02d", minute);

textClock.invalidate();

tickCount = 0;
}

if (!textClock.isMoveAnimationRunning())
{
tickCount++;
if (circle.getArcStart() + 340 == circle.getArcEnd())
{
addStart = 2;
addEnd = 1;
}
else if (circle.getArcStart() + 20 == circle.getArcEnd())
{
addStart = 1;
addEnd = 2;
}
if (circle.getArcStart() > 360 && circle.getArcEnd() > 360)
{
circle.updateArc(circle.getArcStart() - 360, circle.getArcEnd() - 360);
}
circle.updateArc(circle.getArcStart() + addStart, circle.getArcEnd() + addEnd);
}
}
...

根據教程2中所學內容,我們需新增在萬用字元中使用的字元。 在本例中,您需要在供TextAreas使用的字體排印的萬用字元範圍欄位中新增「0-9」。

Further reading
在這一步中,使用了Circle小工具。 如需深入了解Circle小工具的詳細資訊,請閱讀Circle頁面。

第2步:儲存資料

在這一步中,我們將展示如何在螢幕間切換時儲存資料,以及如何擷取儲存的資料。

TouchGFX應用遵循Model-View-Presenter設計模式,為了留存視圖 (即螢幕) 中操作的資料,應將資料發送到Model類別 (透過Presenter)。 關於Model-View-Presenter設計模式的更多資訊,可以在Model-View-Presenter設計模式頁面上找到。

將小時和分鐘新增至模型

模型負責為應用保留資料。 模型中應無臨時資料,如按鈕狀態、目前可見小工具等。

為了透過模型儲存和擷取資料,將受保護的小時和分鐘值新增至模型,並新增公共函數以便存取這些值:

TouchGFX/gui/include/gui/model/Model.hpp
...
public:
void saveHour(int16_t saveHour)
{
hour = saveHour;
}

void saveMinute(int16_t saveMinute)
{
minute = saveMinute;
}

int16_t getHour()
{
return hour;
}

int16_t getMinute()
{
return minute;
}

protected:
int16_t hour;
int16_t minute;
...
Note
model.hpp 檔案還應包括 #include <touchgfx/hal/types.hpp> 以便啟用 int16類型

務必在建構函式中將小時和分鐘初始化:

TouchGFX/gui/src/model/Model.cpp
...
Model::Model() : modelListener(0), hour(0), minute(0)
{
}
...

透過此程式碼,小時和分鐘值得以儲存在模型中,因此可以在螢幕切換時儲存它們 (在切換螢幕時,儲存在螢幕上的資料會遺失)。 由於模型可供所有表現器使用,建議使用此方式在不同表現器(和視圖)之間共用資訊。 UI還可透過模型連接到系統的其餘部分,如硬體周邊裝置和其他軟體模組。

從視窗存取模型

現在,為了從視窗存取模型中的資料,表現器應提供函數來允許Screen1View載入和儲存模型中的資料,如下所示:

TouchGFX/gui/include/gui/screen1_screen/Screen1Presenter.hpp
...
public:
void saveHour(int16_t hour)
{
model->saveHour(hour);
}

void saveMinute(int16_t minute)
{
model->saveMinute(minute);
}

int16_t getHour()
{
return model->getHour();
}

int16_t getMinute()
{
return model->getMinute();
}
...

由於Screen2也應能夠存取模型中的資料,因此將相同程式碼行新增到Screen2Presenter.hpp

模型中的資料

存取模型中小時和分鐘值的程式碼已到位,應更新Screen1Screen2,以便從模型獲取這些值,而不是只使用區域變數。

更新Screen1

現在,我們可以用模型中的值初始化Screen1View中的小時和分鐘,並初始化文字區域的緩衝區:

Screen1View.cpp
...
void Screen1View::setupScreen()
{
Screen1ViewBase::setupScreen();

hour = presenter->getHour();
minute = presenter->getMinute();

Unicode::snprintf(textAreaHourBuffer, TEXTAREAHOUR_SIZE, "%02d", hour);
Unicode::snprintf(textAreaMinuteBuffer, TEXTAREAMINUTE_SIZE, "%02d", minute);
}
...

為了儲存小時和分鐘值,在互動下為兩個儲存按鈕建立的虛擬函數在Screen1View.hpp中實作,並將值存儲在模型中 (透過Presenter):

Screen1View.hpp
...
public:
virtual void buttonSaveHourClicked()
{
presenter->saveHour(hour);
}

virtual void buttonSaveMinuteClicked()
{
presenter->saveMinute(minute);
}
...

Screen1現在可從模型取得小時和分鐘的初始值。

更新Screen2

Screen2還需與模型同步其值。

與Screen1類似,文字時鐘內顯示的初始值必須與模型中的資料一致。

Screen2View.cpp
...
void Screen2View::setupScreen()
{
Screen2ViewBase::setupScreen();

hour = presenter->getHour();
minute = presenter->getMinute();

Unicode::snprintf(textClockBuffer1, TEXTCLOCKBUFFER1_SIZE, "%02d", hour);
Unicode::snprintf(textClockBuffer2, TEXTCLOCKBUFFER2_SIZE, "%02d", minute);
}
...

因此,這可從模型中獲取小時和分鐘值。 在離開螢幕(轉至Screen1上的設定螢幕)時,必須將更新的值發送回模型:

Screen2View.cpp
...
void Screen2View::tearDownScreen()
{
presenter->saveHour(hour);
presenter->saveMinute(minute);

Screen2ViewBase::tearDownScreen();
}
...

這會在轉至設定螢幕前將更新的小時和分鐘值發送到模型。

小應用和教程3到此結束。