Skia4Dephi 的 Demo 程序界面架构分析
前言
Skia 是一個高效率的 2D 畫圖引擎,由 Google 開源出來。目前可以運行在 Android, iOS 和 Win32 上面。
Skia4Delphi 是一個開源的 Delphi 控件,它封裝了對 Skia 的調用,讓 Delphi 的代碼可以很簡單地使用 Skia 來代替 Delphi 原本使用的系統庫(比如 Windows 的 GDI)。尤其是在安卓下,Delphi 自己的支持 FireMonkey 的圖形庫效果不是太好,使用 Skia 代替,基本上不需要修改程序就能獲得更好的效果。比如 TArc 在安卓上圓弧不平滑的問題直接解決。
界面框架
Skia4Delphi 帶來的 Demo 程序,不管是在 Windows 桌面還是在手機上,運行效果都類似一個標準的手機 APP:多層菜單,一層一層點菜單進入,一層一層點回退按鈕退出。
Sika4Delphi 的 Demo 程序包括 2 個,一個是 For VCL 的,一個是 For FMX 的。For VCL 的當然只能在 Windows 平臺上運行,而 For FMX 的,當然可以編譯發布到 Delphi FireMonkey 支持的多個不同的操作系統平臺上,包括 Windows, MacOS, Android, iOS 。
界面框架解析
仔細查看它的 Demo 程序的代碼,發現 VCL 的程序和 FMX 的程序,界面框架的實現方式基本相同,就是把界面的主要元素做成一個基本的 Form,其它 Form 都從這個 Form 繼承。
這種類似手機 APP 的界面,基本元素包括:頂部一個工具欄,工具欄的左側是回退按鈕(一個向左打尖頭符號)。還包括一個底部工具欄,用于顯示一些提示信息。中間則是主要的顯示區域。
不管界面是第幾層,都采用這個框架。每一層,可能有進入下一層的按鈕,點擊后顯示下一層。每一層的回退按鈕,點擊后本層消失,顯示上一層的界面。
抽象出共性后,Skia4Delphi 的 Demo 程序,將這些共性,實現為一個基本的 Form,這里它取名叫做:【TfrmBase】,如下:
TfrmBase = class(TForm)這個 Form 有一個最底層的托盤控件,在 VCL 的 Demo 里面它是一個 TPanel,在 FMX 的 Demo 里面,它是一個 TLayout.。所有的界面控件都擺放在這個托盤上。基于 Delphi 的代碼框架,所謂的擺放,就是:某個界面元素比如 Label1.Parent := 托盤控件。
在此基礎上,這個 TftmBase 還實現了幾個類方法以及用于存放創建的 Form 實例的類變量。
顯示一個新的界面
在設計期,每個新的界面(比如不同的菜單欄,或者顯示不同內容的 Form),都是一個 Form,這些 Form 都繼承自 TfrmBase。因此它們都包含了一個頂部的工具欄以及工具欄左側的回退按鈕。這個工具欄和回退按鈕,又都擺放在最底層的托盤控件上,以它的 VCL Demo 程序為例,擺放在名字叫做?pnlContent 的一個 TPanel 上面。
不管在哪個 Form 里面點擊菜單按鈕需要顯示一個新的 Form,都是調用 TfrmBase 的方法。因此,不管在哪個 Form 里面需要顯示下一層的 Form,都無需額外寫代碼。隨便挑一個按鈕菜單的代碼看看:
procedure TfrmControls.pnlTSkSVGClick(Sender: TObject); beginChildForm<TfrmTSkSVG>.Show; end;上述代碼中, ChildForm 是一個函數:
function TfrmBase.ChildForm<T>: T; varLSelfIndex: Integer; beginAssert(T.InheritsFrom(TfrmBase));LSelfIndex := FCreatedFormsList.IndexOf(Self);if (LSelfIndex >= 0) and (LSelfIndex < FCreatedFormsList.Count - 1) and (FCreatedFormsList[LSelfIndex + 1].ClassType = T) thenExit(T(FCreatedFormsList[LSelfIndex + 1]));Result := CreateForm<T>;TfrmBase(Result).pnlContent.Align := TAlign.alClient;TfrmBase(Result).pnlBack.Visible := FShowingFormsList.Count > 0;FCreatedFormsList.Add(TfrmBase(Result)); end;這個函數把需要顯示的下一層的界面(一個 Form)創建好,并放到類變量?FCreatedFormsList 里面去方便管理。因此,不管在哪一層的界面里面創建了下一層的界面,它的 Form 實例的一個指針,都在?FCreatedFormsList 這個變量里面。
泛型
上述代碼中比較有意思的是,這個被所有真正用于顯示的 Form 繼承的父類基礎 Form,它是沒辦法知道子類的。按照傳統的 Delphi 的語法,這里根本沒法這樣寫。但這里它利用了新的泛型語法,這里直接使用了尖括號加 T,也就是泛型,返回也是 T。因此,真正調用時填入子類 Form 的類名,在父類里面,可以創建子類!
上述代碼中的 Show 方法的代碼則是:
procedure TfrmBase.Show; beginDoShow; end;//這個 DoShow 的代碼: procedure TfrmBase.DoShow; beginpnlTip.Visible := not lblTipDescription.Caption.IsEmpty;if Self = Application.MainForm thenbeginpnlBack.Visible := False;FShowingFormsList.Add(Self);endelsebeginif FShowingFormsList.Count > 0 thenFShowingFormsList.Last.pnlContent.Visible := False;FShowingFormsList.Add(Self);pnlContent.Parent := Application.MainForm; // 關鍵一行代碼在這里end;inherited;pnlContentResize(nil); end;上述代碼,真正顯示下一級 Form 界面的代碼就是這一行:
pnlContent.Parent := Application.MainForm;把這個被創建的下一級 Form 里面作為底層托盤的 Panel 的 Parent 設置為主Form,因此這個 Panel 被擺到主 Form 上面了。
顯示界面的方法
所以,這里顯示的下一層 Form 的界面,并不是下一層 Form 直接 Show 出來,而是把它內部的 Panel 擺放到主 Form 上顯示出來。因此,這里的每一層的 Form 在設計期存在,僅僅是用來作為一個界面模塊的容器!在運行期創建它的實例以后,并沒有顯示它,而是把它當內容(作為托盤的 Panel)直接拿出來顯示到主 Form 上面。
回退按鈕呢?
那么,顯示出來的界面,點了左上角的回退按鈕,它做了什么操作?看代碼:
procedure TfrmBase.pnlBackClick(Sender: TObject); begin{$IF CompilerVersion >= 32}TThread.ForceQueue(nil,procedurebeginCloseForm(Self); //調用 CloseForm 方法,傳入的參數是當前這個 Form 的實例指針。end);{$ELSE}TThread.CreateAnonymousThread(procedurebeginTThread.Queue(nil,procedurebeginCloseForm(Self);end);end).Start;{$ENDIF} end;//真正的關閉 Form 的代碼在這里,這是一個類方法class procedure TfrmBase.CloseForm(const AForm: TfrmBase); varLFormIndex: Integer;LAction: TCloseAction;I: Integer; beginLFormIndex := FShowingFormsList.IndexOf(AForm);if LFormIndex < 0 thenExit;LAction := TCloseAction.caFree;AForm.DoClose(LAction);if LAction = TCloseAction.caNone thenExit;if LFormIndex = 0 thenApplication.TerminateelsebeginLFormIndex := FCreatedFormsList.IndexOf(AForm);Assert(LFormIndex > -1);for I := FCreatedFormsList.Count - 1 downto LFormIndex dobeginFCreatedFormsList[I].Free;FShowingFormsList.Remove(FCreatedFormsList[I]);FCreatedFormsList.Delete(I);end;FShowingFormsList.Last.pnlContent.Visible := True;end; end;在上面的類方法 CloseForm 里面,它根據這個 Form 的實例指針,在之前保存指針的類變量?FCreatedFormsList 里面,把指針從 List 里面刪除掉,并且把這個 Form 對象釋放掉。
Form 對象被釋放掉,它內部被用于顯示界面元素的托盤 Panel 也就被釋放掉了,被它遮住的底下的界面就顯示出來了。
到這里,我們發現,多層界面,其實就是多層 Panel 的層層堆疊。上面一層被釋放掉,下面一層就顯示出來了。
但這些多個 Panel,并不是在設計期,都擠在一個 Form 里面進行設計,而是分別在不同的 Form 里面設計,這樣可以做到界面的模塊化,便于代碼管理。
題外話:我也見過非常復雜很多層顯示界面的程序,所有界面控件都放在一個 Form 里面設計,運行期用代碼決定顯示哪個控件以及每個控件的顯示位置。這樣做,界面上控件太多,擁擠在一起,設計期非常難找到想要的控件,不是一種好的開發模式。
總結
雖然 Skia4Delphi 的 Demo 程序中,運行在 Windows 上的 VCL 程序以及可以運行在手機上的 FMX 程序,都采用了相同的類似手機 APP 的層疊界面的設計模式,但最實用的還是手機 APP,因為屏幕太小,不可能一屏里面還劃分幾個區域,只能是一層一層的界面疊加。因此,這樣的設計模式,在做手機 APP 的時候非常值得學習。實際上它的 FMX 的 Demo 程序的框架,可以直接用于我們自己開發的手機 APP。
之前我自己開發的手機 APP 雖然也是多層界面疊加,但管理界面堆疊層次的代碼就顯得比較羅嗦,沒有這個 Skia4Delphi 的 Demo 程序這樣設計良好的框架,并且將所有界面的顯示和回退的代碼都集中到一個基類里面,在其它界面里面無需重復。
又及:
這個 Skia4Delphi 的 Demo 程序,每個界面模塊是在 Form 里面實現的。要加載一個新的界面就要創建一個 Form 實例。而 Form 占用的資源較多。這里可以采用 Frame 來作為界面設計的基礎框架。我自己寫代碼測試了一下,上述構思,基于 Frame 來做,也是可以實現的。
總結
以上是生活随笔為你收集整理的Skia4Dephi 的 Demo 程序界面架构分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php7 xdebug 性能,PHP 7
- 下一篇: NCC算法简述