Delphi 的繼承檢視功能允許開發者在設計階段預覽表單在不同螢幕尺寸和方向上的呈現效果,這對於建立適應多種裝置的應用程式至關重要。FireUI App Preview 則更進一步,讓開發者可以直接在實體裝置上即時預覽設計變更,這大幅提升了開發效率。文章同時也探討了跨平台開發的核心概念,例如應用程式生命週期管理,以及如何利用 TPlatformServices 和 IFMXApplicationEventService 等 FireMonkey 框架提供的機制來處理平台差異和應用程式事件。理解這些概念對於構建穩健且高效的跨平台應用程式至關重要,特別是在需要針對特定平台特性進行程式碼調整時,例如螢幕方向、感測器和作業系統 API 的使用。
使用繼承檢視(Working with Inherited Views)
在 Delphi 的表單設計中,繼承檢視(Inherited Views)提供了一個非常有用的功能,能夠讓開發者在不同尺寸的螢幕上預覽目前的表單設計。預設情況下,建立的檢視會在預覽表單列表中被勾選,但開發者也可以檢視其他表單,即使它們沒有對應的繼承檢視。這些具有繼承檢視的表單因素會在其左上角顯示一個勾選圖示,如 圖 7.21 所示。
圖 7.21:多裝置預覽(Multi-Device Preview)
如果開發者有多個螢幕,可以考慮將多裝置預覽(Multi-Device Preview)面板移到第二個螢幕上,以便在設計表單時即時預覽不同裝置上的顯示效果。
根據所開發的應用程式需求,開發者可能需要指定設計僅適用於特定的螢幕方向,例如僅支援 Portrait 方向,或是僅支援 Landscape 方向,例如開發街機遊戲時。
設定應用程式的螢幕方向
在 Project Options 中,有一個特殊的 Orientation 分頁,允許開發者為應用程式指定自定義的螢幕方向。這些資訊會被儲存在 IDE 為不同支援平台生成的 manifest 檔案中,並嵌入最終的可執行檔案中。
在裝置上預覽表單
Delphi 的表單設計器提供了「所見即所得」(WYSIWYG)的功能,但開發者可以進一步使用 Delphi 的內建功能,在實體裝置上即時預覽正在設計的表單。首先,需要從 Delphi 範例中編譯並佈署到裝置上,或者下載由 Embarcadero Technologies 發布的 FireUI App Preview 應用程式。
使用 FireUI App Preview
透過 FireUI App Preview,開發者可以在實體裝置上即時預覽正在設計的應用程式。表單設計器會將目前的表單設計以及開發者所做的任何變更,以實時廣播的方式傳送到所有連線的裝置上。此應用程式使用了 Delphi 中的 App Tethering 技術,允許以 Delphi 或 C++Builder 編寫的任意應用程式,無論是桌面還是行動裝置,透過 Wi-Fi 或藍牙進行通訊。
在此場景中,Delphi IDE 執行在 Windows 桌面上,並與執行在任何支援平台上的遠端應用程式進行通訊。可以在 Delphi 的主要安裝資料夾中的 LivePreview 目錄下找到 Windows 和 Mac 版本的 FireUI App Preview 安裝程式。對於行動裝置,可以從行動裝置商店下載該應用程式,或直接從原始碼編譯,該原始碼位於 Delphi 安裝目錄下的 source\Tools\FireUIAppPreview 資料夾中。
啟用 FireUI Live Preview
要在 Delphi 中啟用此功能,需要在 Delphi Options 對話方塊(可在 Tools 功能表中找到)中的 Environment Options | Form Designer | FireUI Live Preview 分頁下進行設定。在此分頁中,可以檢視所有目前已連線的裝置、IDE 廣播所使用的名稱,以及用於保護 IDE 與預覽裝置之間通訊的可選密碼,如 圖 7.22 所示。
圖 7.22:FireUI Live Preview 設定
此外,還可以透過 View | Broadcast to device 功能表專案來切換 Live Preview 功能。預設情況下,此功能是停用的,因為在某些情況下它可能會拖慢 IDE 的啟動速度。
一旦啟動了 FireUI App Preview,它就會在本地網路中搜尋 Delphi IDE 並提供連線選項,如 圖 7.23 所示。如果應用程式無法找到 IDE,可能與無線網路組態有關,或者是因為裝置與執行 IDE 的電腦或虛擬機器不在同一網路和子網路中。事實上,App Tethering 使用 UDP 廣播來建立連線,這要求兩個應用程式必須在同一子網路中執行。
圖 7.23:連線 FireUI App Preview 至 Delphi IDE
連線到 IDE 後,行動裝置上的預覽應用程式會顯示目前在表單設計器中正在設計的表單,如 圖 7.24 所示。在此範例中,使用的是本章前面介紹的 StylesTest4 展示程式。該表單是互動式的,因此可以與其中的元件進行互動,例如在編輯框中輸入內容,但透過事件連線的邏輯則無法透過廣播使用。換句話說,預覽功能僅適用於設計階段,而不適用於需要編譯的原始碼。
圖 7.24:FireUI Live Preview 運作中
最終目標是預覽 UI 設計,而非應用程式邏輯。在任何時候,開發者都可以直接在裝置上執行正在設計的應用程式。FireUI Live Preview 的一個優點是能夠同時將表單廣播到多個不同裝置上,並且是即時的,因此可以立即預覽 UI 設計。
下一步
在下一章中,我們將探討 iOS 和 Android 等行動作業系統上可用的不同框架和元件,以及 FireMonkey 如何幫助我們編寫跨平台程式碼,以抽象出不同 API 之間的差異。
第 8 章:與行動作業系統協同工作
在本章中,我們將從使用 FireMonkey 轉向建立實用的跨平台應用程式,這些應用程式能夠利用高階元件存取行動硬體和作業系統功能,並抽象出底層的行動 API。
本章涵蓋以下主題:
- James Bond 的玩具
- 我執行在哪種裝置上?
- 應用程式的生命週期
- 感知周圍世界
- 拍照
- 使用分享表
- 相機,行動!
- 通知我!
- 瀏覽網頁
- Delphi 語言橋接
本章目標
本章旨在學習如何建立原生的跨平台應用程式,這些應用程式能夠利用不同框架和行動作業系統提供的功能。
深入理解行動裝置作業系統
當代行動裝置硬體的能力與幾年前詹姆斯·龐德(James Bond)所使用的間諜工具並無太大差異。現代手機配備了多種感測器,包括定位、方向、運動和環境光感測器,以及麥克風、喇叭、相機和其他可控硬體。這些硬體資源都可以被行動應用程式所利用。
應用程式與作業系統的互動
行動應用程式完全沉浸在所執行的作業系統中,由作業系統控制應用程式執行的各個方面。在程式碼中,開發者可以對不同的應用程式生命週期事件做出反應,例如啟動、切換到背景執行、傳回前台以及應用程式終止。這些互動本質上是對作業系統傳送的不同事件做出反應並呼叫作業系統的API。
FireMonkey函式庫透過專門的型別、類別和元件封裝了對常見行動作業系統功能的存取,但開發者也可以直接在程式碼中呼叫任意的平台API。有時,這是實作某些功能的唯一方法,例如讓手機振動。幸運的是,大多數時候開發者可以使用跨平台元件和函式庫來概括常見的功能,包括存取地圖或聯絡人、嵌入網頁瀏覽器以及與其他應用程式和服務分享資料。
處理不同行動作業系統的差異
不同的行動作業系統提供可能在其他平台或舊版本同系統上不存在的功能。在這種情況下,開發者可能需要針對特定平台的特定功能編寫程式碼。在本章中,我們將探討如何透過Delphi語言橋接呼叫行動API,以及如何處理iOS或Android特有的功能,例如使用和建立Android服務。
識別執行平台
從相同的原始碼為多個作業系統構建應用程式帶來了獨特的挑戰。開發者的程式碼可能執行在iOS或Android上,只需兩次滑鼠點選即可為不同的目標重新編譯應用程式。某些功能可能存在於給定的平台上,而在其他平台上則不存在。進一步來說,新功能不斷被新增到平台中,因此開發者可能需要知道應用程式正在哪個平台和作業系統版本上執行。
這可以透過System.SysUtils單元中定義的TOSVersion記錄型別來實作。該記錄具有一個類別建構函式,用於例項化其所有欄位。TOSVersion記錄定義了內部的TArchitecture和TPlatform列舉型別以及對應的公共類別屬性,以讀取當前作業系統架構、平台、名稱以及主要和次要版本號。
實作範例:記錄作業系統資訊
我們可以快速構建一個簡單的應用程式,將TOSVersion記錄中的所有資訊記錄到備忘錄中,如下所示:
建立一個新的空白多裝置專案,將主表單儲存為
uFormOSVer,將整個專案儲存為OSVersion。在表單上放置一個工具欄,並新增一個具有醒目標題的標籤,例如“What am I Running On?”。將標籤的
TextSettings.HorzAlign屬性設定為Center,並將其Align屬性設定為Client。在表單上放置一個
TMemo元件,將其對齊到Client,並將其重新命名為MemoLog。為了簡化
TArchitecture和TPlatform列舉型別到字串的轉換,我們可以為這些型別提供輔助類別。向專案新增一個新單元,並將其儲存為uOSVerHelpers。在其中輸入以下類別輔助宣告:type TOSArchHelper = record helper for TOSVersion.TArchitecture function ToString: string; end; TOSPlatHelper = record helper for TOSVersion.TPlatform function ToString: string; end;在實作中,這些輔助方法具有傳回每個可能值的描述性字串的case陳述式。前述是一個例子;另一個非常相似,如下所示:
function TOSArchHelper.ToString: string; begin case self of arIntelX86: Result := 'IntelX86'; arIntelX64: Result := 'IntelX64'; arARM32: Result := 'ARM32'; arARM64: Result := 'ARM64'; else Result := 'Unknown OS Architecture'; end; end;將此單元新增到主表單的uses子句中。
我們還可以在表單類別中定義一個簡單的
Log(S: string)方法,用於在備忘錄中顯示給定的字串:procedure TFormOSVer.Log(S: string); begin MemoLog.Lines.Add(S); end;現在,在表單的
OnCreate事件中,我們可以將所有作業系統版本資訊記錄到備忘錄中,如下所示:procedure TFormOSVer.FormCreate(Sender: TObject); begin Log('OS Version Summary: ' + sLineBreak + TOSVersion.ToString); Log('OS Architecture: ' + TOSVersion.Architecture.ToString); Log('OS Platform: ' + TOSVersion.Platform.ToString); Log('OS Name: ' + TOSVersion.Name); Log('OS Build: ' + TOSVersion.Build.ToString); Log('Version: ' + TOSVersion.Major.ToString + '.' + TOSVersion.Minor.ToString); end;
程式碼解析
上述範例展示瞭如何使用 TOSVersion 記錄來取得當前執行的作業系統資訊,並將這些資訊顯示在應用程式介面上。這對於除錯執行在未知或特殊行動裝置上的應用程式非常有用,同時也可以根據不同的作業系統或同一作業系統的不同版本調整程式碼行為。
圖表說明
此圖示展示了 OSVersion 應用程式分別在 Android 手機和 iOS 模擬器上執行的截圖。
圖表翻譯:
此圖表示 OSVersion 應用程式的工作流程:使用者啟動應用,應用讀取 TOSVersion 資訊並顯示,最後使用者檢視這些資訊。
跨平台應用程式開發與行動作業系統的互動
在開發單一原始碼、多裝置的應用程式時,瞭解應用程式執行的平台至關重要,因為可能需要針對特定平台編寫特定的程式碼。正如本章節所探討的,有多種技術可用於為FireMonkey應用程式提供平台特定的行為。
平台服務的抽象化
FireMonkey函式庫使用了一個常見的模式,即主單元提供跨平台應用程式碼的介面,而以作業系統名稱結尾的特定單元則包含平台特定的實作程式碼。例如,在FMX.Platform單元中,TPlatformServices類別被用作不同平台服務的共同存取點。
檢查服務是否可用
若要檢查某個服務是否可用,可以使用TPlatformServices類別的Current類別屬性。例如,若某個服務透過假設的IFMXAService介面實作,則可以使用以下程式碼片段檢查其是否可用並進行存取:
var
AFMXAService: IFMXAService;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXAService, IInterface(AFMXAService)) then
begin
// 呼叫IFMXAService中定義的方法
// AFMXAService.AMethod;
end
else
// IFMXAService不受支援
程式碼解密:
TPlatformServices.Current.SupportsPlatformService用於檢查某個服務是否受當前平台支援。IFMXAService是一個假設的介面,用於定義某個平台服務的方法。- 如果服務受支援,則可以呼叫該服務的方法。
應用程式生命週期事件
行動作業系統的應用程式生命週期模型各有不同,但對於行動平台來說,正確處理應用程式從前景執行切換到背景執行以及傳回前景執行的情況至關重要。
註冊應用程式生命週期事件處理器
FireMonkey透過FMX.Platform單元中定義的不同型別的應用程式事件,提供了一個處理應用程式生命週期的共同抽象化。若要接收應用程式生命週期事件,需要將事件處理器函式的參考傳遞給IFMXApplicationEventService的SetApplicationEventHandler方法。
首先,需要使用前面描述的藍圖檢查TPlatformServices類別中是否有所需的服務。事件處理器可以是任何接受TApplicationEvent和TObject引數並傳回布林值的方法。
範例:建立測試應用程式
- 在IDE中選擇新的多裝置應用程式 - Delphi選項,並選擇空白應用程式範本。
- 將主表單單元儲存為
uFormLifecycle,將整個專案儲存為AppLifecycle,並將主表單的Name屬性更改為FormLifecycle。 - 在表單上放置一個
TToolbar和一個TMemo控制項。 - 將備忘錄重新命名為
MemoLog並對齊到客戶端。在表單的類別中宣告一個Log(S: string)方法,以將訊息輸出到備忘錄。
事件處理器實作
function TFormLifecycle.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
begin
case AAppEvent of
TApplicationEvent.FinishedLaunching:
Log('應用程式事件:完成啟動');
TApplicationEvent.BecameActive:
Log('應用程式事件:變為活動');
TApplicationEvent.WillBecomeInactive:
Log('應用程式事件:將變為非活動');
TApplicationEvent.EnteredBackground:
Log('應用程式事件:進入背景');
TApplicationEvent.WillBecomeForeground:
Log('應用程式事件:將進入前景');
TApplicationEvent.WillTerminate:
Log('應用程式事件:將終止');
TApplicationEvent.LowMemory:
Log('應用程式事件:記憶體不足');
TApplicationEvent.TimeChange:
Log('應用程式事件:時間變更');
TApplicationEvent.OpenURL:
Log('應用程式事件:開啟URL');
else
Log('未知的應用程式事件');
end;
Result := True;
end;
註冊事件處理器
procedure TFormLifecycle.FormCreate(Sender: TObject);
var
AFMXApplicationEventService: IFMXApplicationEventService;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, AFMXApplicationEventService) then
AFMXApplicationEventService.SetApplicationEventHandler(HandleAppEvent)
else
Log('應用程式事件服務不受支援。');
end;
平台差異與實務建議
在iOS和Android上執行上述範例應用程式時,會發現兩者的應用程式生命週期事件並不完全相同。例如,在Android上接收到的第一個應用程式生命週期事件是FinishedLaunching,而在iOS上則從未接收到該事件。
iOS與Android的應用程式生命週期事件比較
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Delphi 繼承檢視與跨平台應用
package "物件導向程式設計" {
package "核心概念" {
component [類別 Class] as class
component [物件 Object] as object
component [屬性 Attribute] as attr
component [方法 Method] as method
}
package "三大特性" {
component [封裝
Encapsulation] as encap
component [繼承
Inheritance] as inherit
component [多型
Polymorphism] as poly
}
package "設計原則" {
component [SOLID] as solid
component [DRY] as dry
component [KISS] as kiss
}
}
class --> object : 實例化
object --> attr : 資料
object --> method : 行為
class --> encap : 隱藏內部
class --> inherit : 擴展功能
inherit --> poly : 覆寫方法
solid --> dry : 設計模式
note right of solid
S: 單一職責
O: 開放封閉
L: 里氏替換
I: 介面隔離
D: 依賴反轉
end note
@enduml圖表翻譯: 此圖示比較了iOS和Android在應用程式生命週期事件上的差異。顯示了從啟動應用程式到進入背景再傳回前景的過程。
綜上所述,開發跨平台行動應用程式時,瞭解不同平台之間的差異對於提供最佳使用者經驗至關重要。透過使用FireMonkey提供的抽象化機制,開發者可以編寫更具可移植性的程式碼,同時針對特定平台進行最佳化。