教程3:多個螢幕的應用
在本教程中,您將學習如何在一個應用中創建多個螢幕以及在兩個螢幕之間共用資料。 我們將創建一個類比時鐘的應用,它將用一個螢幕來設置小時和分鐘並將時間傳遞到另一個螢幕。
您還將學習如何使用TouchGFX Designer基於交互(如螢幕切換)創建動畫。
本教程使用的圖像可從此連結下載。 將檔案解壓縮到硬碟上的目錄。
第1步:設置兩個螢幕
像教程2中所做的一樣新建一個專案。 同樣地,圖形是為480x272的解析度而設計的。 如果專案基於“STM32F746G探索套件”的應用範本,專案將使用此解析度。 如果您有具有其他解析度的開發板,應能修改圖形以適應您的需求。 如果沒有開發板,則直接基於“模擬器”應用範本,並務必輸入480x272作為解析度。
設置Screen1
要創建的第一個螢幕是Screen1,可以在該螢幕上設置小時和分鐘值,然後發送到顯示運行的時鐘的螢幕。 Screen1顯示如下,包含兩個文字方塊,其中包含時鐘所需的時間。 使用向上和向下箭頭調整文字方塊中的值。 使用對應文字方塊下方的“save”按鈕保存選擇的值並將它們傳遞到時鐘。 最後,按下“Clock”按鈕切換到顯示時鐘的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;
}
circle.updateArc(circle.getArcStart() + addStart, circle.getArcEnd() + addEnd);
}
}
...
根據教程2中所學內容,我們需要在萬用字元中添加使用的字元。 在本例中,您需要在供TextAreas使用的字體排印的“萬用字元範圍”列中添加“0-9”。
Further reading
第2步:保存資料
在這一步中,我們將展示在螢幕間切換時如何保存資料,以及如何檢索保存的資料。
TouchGFX應用遵循Model-View-Presenter設計模式,為了保存視圖(即螢幕)中操作的資料,應將資料發送到Model類(通過表現器)。 關於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中的小時和分鐘,並初始化TextAreas的緩衝區:
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
中實現,並將值存儲在模型中(通過簡報者):
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到此結束。