教程3:多個螢幕的應用
在本教程中,您將學習如何在一個應用中建立多個螢幕,以及在兩個螢幕之間共用資料。 我們將建立一個模擬時鐘的應用,它將用一個螢幕來設定小時和分鐘,並將時間傳遞到另一個具有運作中時鐘的螢幕。
您還將學習如何使用TouchGFX Designer根據互動 (如螢幕切換) 建立動畫。
本教程使用的圖像可從此連結下載。 將檔案解壓縮到硬碟上的目錄。
第1步:設定兩個螢幕
如教程2中所做,建立一個新專案。 同樣地,圖形是以480x272的解析度進行設計。 如果專案是以「STM32F746G探索套件」的應用程式範本為基礎,專案將使用此解析度。 如果您有具有其他解析度的Demo開發板,應能修改圖形以符合您的需求。 如果沒有Demo開發板,則直接以「模擬器」應用程式範本為基礎,且務必輸入480x272作為解析度。
設定Screen1
要建立的第一個螢幕是Screen1,可以在該螢幕上設定小時和分鐘值,然後發送到具有運作中時鐘的螢幕。 Screen1顯示如下,包含兩個文字方塊,其中包含時鐘所需的時間。 使用向上和向下箭頭調整文字方塊中的值。 使用對應文字方塊下方的「儲存」按鈕儲存所選值,並將它們傳遞到時鐘。 最後,按下「時鐘」按鈕切換到顯示時鐘的Screen2。
我們首先為應用插入背景和文字區域。 插入小工具並更新屬性,如下列圖片和表格所示。
位置 | 小工具 | 屬性 |
---|---|---|
1 | 圖像 |
|
2 | TextArea |
|
3 | TextArea |
|
4 | TextArea |
|
5 | TextArea |
|
然後,依下面的圖片和表格所示,將按鈕插入應用。
位置 | 小工具 | 屬性 |
---|---|---|
1 | 按鈕 |
|
2 | 按鈕 |
|
3 | 按鈕 |
|
4 | 按鈕 |
|
5 | 含標籤的按鈕 |
|
6 | 含標籤的按鈕 |
|
7 | 含標籤的按鈕 |
|
在設定了圖形元素後,下一步是為按鈕新增觸發條件,它會調整所選值並儲存:
互動 | 屬性 |
---|---|
點擊「小時值增加」按鈕 |
|
點擊「小時值減少」按鈕 |
|
點擊「分鐘值增加」按鈕 |
|
點擊「分鐘值減少」按鈕 |
|
點擊「儲存小時值」按鈕 |
|
點擊「儲存分鐘值」按鈕 |
|
如果您按下「產生程式碼」或「執行模擬器」按鈕,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之前,必須建立一個新螢幕。 這是透過TouchGFX Designer中的「新增螢幕」按鈕 (1) 完成的,如下圖所示。
在進入「screen2」時,我們要讓時鐘和圓移動到它們的位置,要從螢幕以外移動到視圖中:時鐘從左側移入,圓從右側移入。 因此,兩個小工具最初會位於TouchGFX Designer的畫布之外。
應按照下面的圖片和表格所示放置小工。
位置 | 小工具 | 屬性 |
---|---|---|
1 | 圖像 |
|
1 | 按鈕 |
|
3 | TextArea |
|
4 | 圓 |
|
在新增所有小工具後,螢幕應如下圖所示。 TextArea和Circle位於螢幕之外,因此不可見。
在螢幕之間切換
現在,我們要新增功能,以便在兩個螢幕之間切換。 為此,我們為Screen1上的「時鐘」按鈕和Screen2上的「設定」按鈕指派互動。 此外,我們要在進入Screen2時讓位於Screen2上螢幕區之外的兩個元素移動到位。
為此,為Screen1新增下列互動:
互動 | 屬性 |
---|---|
切換到「Screen2」 |
|
此外,為Screen2新增下列互動:
互動 | 屬性 |
---|---|
切換到「Screen1」 |
|
將圓移動到位 |
|
將文字時鐘移入到位 |
|
在新增互動後,螢幕如下圖所示:
為了在執行階段更新時鐘和使圓移動,我們使用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
第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
。
模型中的資料
存取模型中小時和分鐘值的程式碼已到位,應更新Screen1
和 Screen2
,以便從模型獲取這些值,而不是只使用區域變數。
更新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到此結束。