步驟1:設置兩個屏幕
多屏應用
在本教程中,您將學習如何在應用程序中創建多個屏幕以及如何在兩個屏幕之間共享數據。我們將創建一個模擬時鍾的應用程序,它將使用一個屏幕設置小時和分鍾,並將設置的時間傳遞給另一個帶有運行時鍾的屏幕。
您還將學習如何使用TouchGFX Designer根據交互(例如屏幕更改)創建動畫。
可以從此鏈接下載本教程中將使用的圖像 。將文件解壓縮到磁盤上的目錄。
設置屏幕1
創建的第一個屏幕是Screen1,它是可以設置小時和分鍾並將其與運行時鍾一起發送到屏幕的屏幕。屏幕1如下所示,其中包含兩個文本框,其中包含所需的時鍾時間。要調整框中的值,請使用向上和向下箭頭。為了保存所選值並將其傳遞到時鍾上,使用了相應框下方的保存按鈕。最后,通過按時鍾按鈕,隨時鍾切換到Screen2。
插入背景和文本區域的操作如下圖和表格所示。
位置 | 小部件 | 物產 |
1個 | 圖片 |
|
2 | 文字區 |
|
3 | 文字區 |
|
4 | 文字區 |
|
5 | 文字區 |
|
這些按鈕將插入到應用程序中,如下圖和表所示。
位置 | 小部件 | 物產 |
1個 | 紐扣 |
|
2 | 紐扣 |
|
3 | 紐扣 |
|
4 | 紐扣 |
|
5 | 帶標簽的按鈕 |
|
6 | 帶標簽的按鈕 |
|
7 | 帶標簽的按鈕 |
|
通過設置圖形元素,下一步是添加按鈕的觸發器,按鈕將調整所選值並保存它們:
相互作用 | 物產 |
單擊小時增加按鈕 |
|
單擊小時減少按鈕 |
|
單擊分鍾向上按鈕 |
|
單擊分鍾向下按鈕 |
|
單擊“節省時間”按鈕 |
|
單擊“保存分鍾”按鈕 |
|
利用設計人員生成的虛擬功能,我們首先集成了箭頭按鈕的四個功能。為了跟蹤小時和分鍾的值,還添加了兩個計數器。
因此,以下代碼應添加到Screen1View.hpp
Screen1View.hpp
...
public:
...
virtual void buttonHourUpClicked();
virtual void buttonHourDownClicked();
virtual void buttonMinuteUpClicked();
virtual void buttonMinuteDownClicked();
protected: int16_t hour; int16_t minute; ...
當按下箭頭時,相應的值將更改為適合時鍾的值。
應當以以下方式添加這四個功能的邏輯。
Screen1View.cpp
... 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(); }
設置屏幕2
第二個屏幕Screen2是運行時鍾的放置位置,從Screen1中保存的值開始。除時鍾外,Screen2還包含一個圍繞時鍾設置動畫的圓圈,表示時鍾正在運行。最后,返回到Screen1,實現了將屏幕更改為Screen1的按鈕。
在將元素添加到Screen2之前,必須創建一個新屏幕。這是通過TouchGFX Designer中的添加屏幕按鈕完成的,如下圖所示。
當進入“ screen2”時,時鍾和動畫都通過進入視圖而動畫到其位置,時鍾從左側移入,圓圈從右側移入。因此,這兩個小部件最初放置在TouchGFX Designer中的畫布外部。
窗口小部件的放置應按照下面的圖片和表格進行。
位置 | 小部件 | 物產 |
1個 | 圖片 |
|
2 | 紐扣 |
|
3 | 文字區 |
|
4 | 圈 |
|
在屏幕之間切換
屏幕1上的時鍾按鈕和屏幕2上的配置按鈕需要交互才能切換屏幕。此外,輸入Screen2時,需要將放置在Screen2上屏幕區域之外的兩個元素移到適當位置。
將以下交互添加到Screen1:
相互作用 | 物產 |
“更改為Screen2” |
|
還將以下交互添加到Screen2:
相互作用 | 物產 |
更改為“ Screen1” |
|
將圓移到位 |
|
將文字時鍾移到位 |
|
要在運行時更新時鍾並為圓圈設置動畫,請handleTickEvent
使用虛擬功能 。
handleTickEvent
由TouchGFX框架定期調用,使它能夠動態更新活動屏幕中的元素,在這種情況下將是時鍾和循環。
類似於Screen1,一個小時和一個分鍾計數器用於跟蹤時鍾。由於 handleTickEvent
調用頻率比應更新的時鍾頻率高,因此添加了tickCounter來確定時鍾更新之間的滴答數。為了更新圓弧的角度,使用了addStart和addEnd函數。handleTickEvent
如下所示,將函數和變量添加到Screen2View.hpp中。
Screen2View.hpp
public:
...
virtual void handleTickEvent();
protected: int16_t hour; int16_t minute; int16_t tickCount; int16_t addStart; int16_t addEnd; ...
集成了handleTickEvent
Screen2View.cpp,從而更新了時鍾和圓圈的代碼如下所示
Screen2View.hpp
... 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.invalidate(); circle.setArc(circle.getArcStart() + addStart, circle.getArcEnd() + addEnd); circle.invalidate(); } } ...
- 請記住,將用於小時和分鍾的印刷術的通配符范圍
0-9
列添加到列中,以使TouchGFX生成所有數字的字形。
在這一步中,使用了小部件Circle來了解有關Circle的更多信息,文章 Circle描述了該小部件及其屬性。
在下一步中,將添加Screen1上保存按鈕的代碼,以允許在Screen1和Screen2之間共享數據。
步驟2:儲存資料
在此步驟中,我們將顯示在屏幕之間切換時如何保存數據以及如何檢索保存的數據。
在模型中保存數據
為了將在視圖(即屏幕)中操作的數據持久化,應該將數據發送到模型(通過演示者)。有關Model-View-Presenter模式的更多信息,可以在Internet上的許多地方找到,例如Wikipedia,或在 “屏幕概念和Model-View-Presenter”一文中找到。
將小時和分鍾添加到模型
該模型負責保存應用程序的數據。臨時數據(例如按鈕狀態,當前可見的小部件等)不應在模型中。
要通過模型保存和檢索數據,請向模型添加受保護的小時和分鍾值,以及用於訪問這些值的公共函數:
型號
... 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; ...
確保在構造函數中初始化小時和分鍾:
模型.cpp
... Model::Model() : modelListener(0), hour(0), minute(0) { } ...
使用此代碼,小時和分鍾在模型中占有一席之地。由於該模型適用於所有演示者,因此建議在演示者(和視圖)之間共享信息。該模型也是UI能夠連接到系統其余部分(例如硬件外圍設備和其他軟件模塊)的地方。
從視圖訪問模型
現在,為了從視圖訪問模型中的數據,演示者應該提供允許Screen1View從模型中加載和保存數據的函數,如下所示:
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以從Model中獲取這些值,而不是僅使用局部變量。
更新畫面1
現在,我們可以使用模型中的值在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中實現了在交互作用下為兩個保存按鈕創建的虛擬函數,並將這些值存儲在模型中(通過演示者):
Screen1View.hpp
... public: virtual void buttonSaveHourClicked() { presenter->saveHour(hour); } virtual void buttonSaveMinuteClicked() { presenter->saveMinute(minute); } ...
現在,Screen1從模型中獲取小時和分鍾的初始值。
更新Screen2
Screen2還需要將其值與模型同步。
與屏幕1相似,文本時鍾中顯示的初始值必須與來自模型的數據匹配。
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的內容,但是需要進一步改進,並在下一步中找到想法。
在下一個教程中,將介紹小部件“自定義容器”和“滾輪”,您將學習為小部件創建自定義行為。繼續 教程4。
步驟3:進一步改進的想法
即使我們有一個運行中的應用程序,也可能缺少一些東西。這些事情值得考慮,如果不在本應用程序中,那么也許在現實世界中的應用程序中也應考慮。
如果模型支持某種警報功能,則每次時鍾更新時,Screen2都必須將更新的時間發送給模型。
- 提示:Screen2View :: handleTickEvent()。
- 提示:如果小時/分鍾實際上已更改,則只有在更新時才通知模型,而不是在每個刻度上都將更新通知模型。
同樣,可以通過應用程序中的其他一些后台作業或硬件在模型中更改時鍾。
- 提示:在更新值之前,先在滴答處理程序中從模型中讀取分鍾/小時,然后立即將它們寫回到模型中。
- 提示:考慮添加一種鎖定機制,以確保沒有其他過程在更改模型中的時鍾值
如果時鍾長時間運行,則弧開始和弧結束可能會溢出,因為弧被存儲為具有較高值的整數。
- 提示:如果getArcStart()> 360,則從圓弧起點和圓弧終點都減去360是安全的。
將應用程序更改為在時鍾屏幕而不是配置屏幕上啟動。
- 提示:選擇Screen2時,只需單擊“設置為啟動屏幕”按鈕。
輸入Screen2后立即開始計時(而不是在動畫結束之后)。
- 提示:移動“ tickCounter ++;” 在“ if(!textClock.isMoveAnimationRunning())”之外。
屏幕1和屏幕2是此小示例可接受的名稱。考慮將屏幕名稱更改為更好的名稱。
- 提示:例如ClockConfigScreen和RunningClockScreen。
- 提示:甚至可以使用ClockConfig和RunningClock。
更新視圖中的通配符緩沖區涉及對snprintf()的兩次調用。考慮將這些行移至單獨的函數,以減少應用程序中的重復代碼量。
- 提示:添加函數“ void updateClockText();” 在Screen1View.hpp中,並在Screen1View.cpp中添加實現。對Screen2View.hpp和Screen2View.cpp執行相同的操作。
更改屏幕過渡效果。
- 提示:如果過渡是“北”以轉到一個屏幕,則返回的過渡應為相反的方向(“南”),以使兩個屏幕彼此相鄰放置。
【來源】