開發(fā)實(shí)戰(zhàn):TMS FMX UI Pack v3.7.7.5總結(jié))
簡(jiǎn)介TMS FMX UI Pack v3.7.7.5 是一套面向 Delphi 開發(fā)者的跨平臺(tái) UI 組件庫(kù)基于 FireMonkey 框架深度優(yōu)化尤其適合需要兼顧 Windows、macOS 與 Android 的應(yīng)用界面開發(fā)場(chǎng)景。完整源碼版提供超過 450 個(gè)組件從基礎(chǔ)控件到圖表、日歷等復(fù)雜功能模塊一應(yīng)俱全開發(fā)者可查看并修改底層源碼按項(xiàng)目需求靈活定制控件行為和視覺風(fēng)格也便于在 Delphi 11 環(huán)境下進(jìn)行性能優(yōu)化與問題排查。壓縮包采用 RAR 格式整體約 56.74MB內(nèi)容圍繞組件的源單元、示例工程及說明文檔組織安裝和引用后可快速融入現(xiàn)有項(xiàng)目。目前已有 269 人學(xué)習(xí)使用適用于希望減少重復(fù) UI 搭建工作、打造專業(yè)一致界面效果的中高級(jí) Delphi 移動(dòng)與桌面應(yīng)用開發(fā)者。 Delphi開發(fā)圈子里TMS的組件包基本屬于“繞不開”的存在。我最早接觸TMS是從VCL時(shí)代的TAdvStringGrid開始的后來項(xiàng)目全面轉(zhuǎn)向FMX跨平臺(tái)后TMS FMX UI Pack就成了我工具箱里的常駐成員。這兩天在整理舊項(xiàng)目時(shí)正好翻到了v3.7.7.5這個(gè)版本的完整源碼包結(jié)合近幾年在Windows、macOS、Android三端實(shí)戰(zhàn)中踩過的坑和積累的經(jīng)驗(yàn)寫一篇完整的使用總結(jié)。如果你是正在做FMX跨平臺(tái)開發(fā)、又被原生控件丑到懷疑人生的朋友這篇文章應(yīng)該能幫你省下不少調(diào)研時(shí)間。從Delphi 10.3開始FMX框架已經(jīng)相當(dāng)成熟但官方自帶的控件庫(kù)在UI表現(xiàn)力上一直比較“素”。TMS FMX UI Pack的出現(xiàn)本質(zhì)上就是把這層短板補(bǔ)齊——它提供了幾十個(gè)高質(zhì)量的可視化組件從數(shù)據(jù)表格、圖表、日歷到側(cè)邊欄、卡片、按鈕幾乎覆蓋了企業(yè)級(jí)應(yīng)用UI的絕大多數(shù)場(chǎng)景。v3.7.7.5這個(gè)版本在這條產(chǎn)品線里算是一個(gè)相當(dāng)穩(wěn)定的迭代兼容性、性能優(yōu)化和組件豐富度都處于平衡狀態(tài)。對(duì)于“full source”這個(gè)關(guān)鍵詞很多人可能理解成“白嫖版”其實(shí)不是——TMS官方一直是提供Full Source授權(quán)方式的購(gòu)買后你能拿到完整的.pas源碼這對(duì)于需要深度定制控件行為、或者有安全審計(jì)需求的團(tuán)隊(duì)來說價(jià)值遠(yuǎn)大于單純的“能編譯過”。1. 為什么我最終選擇了TMS FMX UI Pack1.1 從一次真實(shí)項(xiàng)目選型說起去年接手了一個(gè)進(jìn)銷存管理的跨平臺(tái)項(xiàng)目需求方要求Windows PC端和Android平板端共用一套業(yè)務(wù)邏輯代碼UI層面盡量保持一致。當(dāng)時(shí)擺在我面前的選擇其實(shí)不少FMX原生控件、TMS FMX UI Pack、Delphi自帶的Style適配、或者干脆針對(duì)兩端各寫一套界面。我試著用原生FMX控件搭原型只做了主界面和兩個(gè)二級(jí)頁(yè)面效果說實(shí)話有點(diǎn)“簡(jiǎn)陋過頭”——不是因?yàn)楣δ懿恍卸强丶囊曈X層次感不夠數(shù)據(jù)表格的美觀度和交互流暢性都達(dá)不到客戶的預(yù)期。對(duì)比之下TMS FMX UI Pack的TMS FMX Grid在視覺表現(xiàn)和功能完整度上幾乎是無(wú)縫替代原生TGrid的最佳選擇尤其是虛擬滾動(dòng)、混合行類型、單元格內(nèi)嵌控件這些細(xì)節(jié)省下來的工作量是肉眼可見的。1.2 全源碼版本的價(jià)值到底在哪里我個(gè)人的建議是只要預(yù)算允許優(yōu)先選擇Full Source授權(quán)。原因很簡(jiǎn)單——你買的不是“能用的控件”而是“能改的控件”。舉個(gè)實(shí)際例子TMS FMX Grid默認(rèn)的排序指示器樣式是箭頭但客戶要求在平板端換成“升序/降序顏色區(qū)分”的組合樣式。如果是編譯版組件這件事基本無(wú)解你只能通過事件二次繪制來做間接實(shí)現(xiàn)代碼繞且不清爽。有了源碼就不一樣了直接找到SortIndicator的繪制方法改掉默認(rèn)行為重新編譯安裝即可后續(xù)維護(hù)也完全可控。另外full source在調(diào)試時(shí)也有巨大優(yōu)勢(shì)你可以直接進(jìn)入組件源碼內(nèi)部設(shè)置斷點(diǎn)查看繪制邏輯和數(shù)據(jù)綁定流程。運(yùn)營(yíng)階段若遇到偶發(fā)的列表閃爍問題順著源碼排查效率遠(yuǎn)高于黑盒調(diào)試。1.3 組件覆蓋范圍速覽v3.7.7.5的組件數(shù)量相當(dāng)可觀。按照我日常使用的頻率來劃分大致有幾類數(shù)據(jù)展示類TMS FMX Grid、TMS FMX TreeView、TMS FMX ListView圖表可視化TMS FMX Chart、TMS FMX SparkChart輸入與交互TMS FMX Edit、TMS FMX ComboBox、TMS FMX DateTimePicker布局與導(dǎo)航TMS FMX Sidebar、TMS FMX CardPanel、TMS FMX TabControl系統(tǒng)集成TMS FMX Localization、TMS FMX InAppPurchase其中Grid和Chart是我項(xiàng)目中的絕對(duì)主力后面會(huì)專門展開講。2. 環(huán)境準(zhǔn)備與安裝避坑指南2.1 版本兼容性核對(duì)先強(qiáng)調(diào)一個(gè)原則不管是什么組件包安裝前一定要核對(duì)IDE版本和組件版本之間的兼容關(guān)系。TMS FMX UI Pack v3.7.7.5官方支持的是RAD Studio 10.3 Rio到11.x Alexandria這代產(chǎn)品具體的版本號(hào)在安裝包內(nèi)的README或者TMS官網(wǎng)的Compatibility Matrix里都有明確標(biāo)注。我當(dāng)時(shí)踩過的一個(gè)坑是剛開始在一臺(tái)裝著Delphi 10.2 Tokyo的機(jī)器上直接安裝結(jié)果編譯報(bào)了一大堆“Unit not found”的錯(cuò)誤排查后才發(fā)現(xiàn)是IDE版本太老某些FMX框架內(nèi)部接口對(duì)不上。后來升級(jí)到10.3.3一次通過。注意如果你還在用10.2或更早版本的Delphi我的建議是優(yōu)先升級(jí)IDE而不是降級(jí)組件版本。老版本IDE 新組件包的兼容性風(fēng)險(xiǎn)遠(yuǎn)大于新IDE帶來的系統(tǒng)資源開銷。2.2 安裝全過程與常見編譯錯(cuò)誤TMS FMX UI Pack的安裝比較傳統(tǒng)從官網(wǎng)下載安裝包Full Source版本會(huì)額外附帶Source目錄運(yùn)行后選擇對(duì)應(yīng)的IDE版本32位或64位按需處理然后等待編譯安裝。不過很多人在這步容易栽跟頭。安裝包默認(rèn)使用的是當(dāng)前IDE的默認(rèn)編譯器配置如果在此之前你手動(dòng)調(diào)整過Library路徑或者安裝了其他第三方組件可能會(huì)出現(xiàn)重復(fù)單元定義或路徑優(yōu)先級(jí)沖突。解決思路是這樣的先在IDE的Tools Options Language Delphi Library中把默認(rèn)的Library路徑檢查一遍確認(rèn)沒有殘留的舊版本TMS路徑。然后把安裝包提供的Source路徑添加進(jìn)來并且確保這個(gè)路徑排在Library列表的靠前位置。安裝完成后務(wù)必驗(yàn)證一下新建一個(gè)空白FMX項(xiàng)目隨便拖一個(gè)TMS FMX Grid到窗體上編譯運(yùn)行。如果這一步穩(wěn)定通過說明安裝基本沒問題。2.3 全版本共存的策略很多團(tuán)隊(duì)會(huì)同時(shí)維護(hù)多個(gè)版本的Delphi IDE也會(huì)用到多個(gè)版本的TMS組件。我給自己的環(huán)境定了三條規(guī)則實(shí)測(cè)下來非常管用每個(gè)IDE版本單獨(dú)安裝一份TMS組件路徑互不混淆源碼包放獨(dú)立目錄不塞進(jìn)IDE安裝目錄內(nèi)更新組件版本前先備份當(dāng)前可用的Library路徑配置尤其是第三條TMS經(jīng)常在不同小版本間調(diào)整內(nèi)部接口回退版本時(shí)需要把舊版本的Source路徑恢復(fù)回去沒有備份就只能靠記憶去填非常折騰。3. 核心組件深度實(shí)戰(zhàn)3.1 TMS FMX Grid從基本綁定到高級(jí)應(yīng)用TMS FMX Grid是我用得最重的組件沒有之一。它本質(zhì)上是一個(gè)高性能的虛擬化表格組件支持本地?cái)?shù)據(jù)綁定、主從架構(gòu)、行/列自由合并、單元格內(nèi)嵌進(jìn)度條、圖片和按鈕等?;A(chǔ)使用方式非常直接。假設(shè)你有一個(gè)TFDMemTable作為數(shù)據(jù)源只需要指定Grid的DataSource屬性表格就會(huì)自動(dòng)拉取數(shù)據(jù)并渲染。普通程序員到這一步就可以“交差”了但實(shí)際項(xiàng)目里我們通常還要處理列寬自適應(yīng)、行高按內(nèi)容伸縮、凍結(jié)列、橫向滾動(dòng)吸底這些細(xì)節(jié)。我在做倉(cāng)儲(chǔ)管理界面時(shí)遇到過一個(gè)問題表格列數(shù)多達(dá)14列在平板橫屏下依然需要橫向滾動(dòng)。TMS FMX Grid的列凍結(jié)功能完美解決了這個(gè)問題把“商品名稱”“條碼”這類關(guān)鍵列設(shè)置成凍結(jié)狀態(tài)用戶滾動(dòng)時(shí)它們始終保持可見體驗(yàn)提升很明顯。設(shè)置方式在列集合中找到需要凍結(jié)的列把它的Frozen屬性設(shè)為True然后指定FrozenColumnsCount。源碼版本中你甚至可以自定義凍結(jié)列和普通列之間的分界線樣式。另一個(gè)非常實(shí)用的功能是“可編輯單元格”。直接在Grid上啟用Editing屬性再配合TMS FMX Edit作為默認(rèn)編輯器用戶就能在表格內(nèi)直接修改數(shù)據(jù)修改完成后通過OnCellEditingDone事件回調(diào)進(jìn)行數(shù)據(jù)校驗(yàn)和保存。我實(shí)際測(cè)試過在1000行數(shù)據(jù)規(guī)模的界面中編輯操作幾乎無(wú)卡頓。3.2 圖表組件TMS FMX Chart的定制思維Chart組件在進(jìn)銷存里也是高頻剛需尤其是近30天銷售趨勢(shì)、品類占比這類分析圖。TMS FMX Chart的API設(shè)計(jì)比Delphi自帶的TChart要清爽不少它提供了一套FMX原生的可繪制模型性能好跨平臺(tái)表現(xiàn)一致?;A(chǔ)的折線圖三行代碼搞定var series: TMSChartLineSeries; begin series : TMSChartLineSeries.Create(Self); series.Title : 銷售額; Chart1.AddSeries(series); series.AddXY(1, 1200); series.AddXY(2, 1480); series.AddXY(3, 1320); end;但真實(shí)項(xiàng)目里我們很少直接用硬編碼數(shù)據(jù)更多是綁定數(shù)據(jù)源。TMS FMX Chart支持從TDataSet或泛型列表加載數(shù)據(jù)也支持動(dòng)態(tài)添加多個(gè)Series做對(duì)比。我在做庫(kù)存周轉(zhuǎn)率對(duì)比頁(yè)面時(shí)一次性掛了5個(gè)Series、涉及2000多個(gè)數(shù)據(jù)點(diǎn)圖形渲染依然流暢。配色方面默認(rèn)樣式比較樸素。源碼版本里可以直接改Series的Brush和Stroke屬性也可以在繪制事件中做更精細(xì)的顏色過渡。我自己常用的一個(gè)技巧是給系列的數(shù)量動(dòng)態(tài)生成一個(gè)“漸變色板”避免兩個(gè)系列顏色太接近導(dǎo)致圖表難以閱讀。3.3 日歷與日期選擇TMS FMX Calendar的降維打擊如果你做過酒店預(yù)訂、排班管理或者任何涉及日期范圍的業(yè)務(wù)一定會(huì)對(duì)Picker類控件特別敏感。FMX自帶的TDateEdit功能太基礎(chǔ)不支持范圍選擇也無(wú)法自定義日期的視覺狀態(tài)。TMS FMX Calendar補(bǔ)齊了這些缺口。它支持的選擇模式包括單日、多日、連續(xù)區(qū)間也可以通過事件自定義某個(gè)日期是否可點(diǎn)擊。我在這套組件上做排班系統(tǒng)時(shí)把工作日、休息日、節(jié)假日在日歷上用不同顏色標(biāo)記出來直接給用戶一個(gè)直觀的“可排班/不可排班”視覺反饋。這種細(xì)節(jié)用原生控件實(shí)現(xiàn)起來非常痛苦用到TMS Calendar只需要處理對(duì)應(yīng)的事件回調(diào)即可。在Android端實(shí)際使用的過程中TMS FMX Calendar的觸控體驗(yàn)也表現(xiàn)得不錯(cuò)——滑動(dòng)切換月份、多點(diǎn)縮放這些交互都有基礎(chǔ)支持未做額外定制的情況下已經(jīng)能提供接近原生應(yīng)用的流暢度。4. 跨平臺(tái)部署中的關(guān)鍵問題排查4.1 Android端中文字體渲染問題FMX在Android端的中文字體渲染一直是個(gè)敏感話題。使用TMS FMX Grid和Chart時(shí)如果字體設(shè)置不當(dāng)會(huì)出現(xiàn)中文顯示為“豆腐塊”或者鋸齒明顯的問題。我的解決方案是在程序啟動(dòng)時(shí)全局設(shè)置一次字體TFontManager.SetDefaultFont(MiSans);MiSans或者HarmonyOS Sans這類字體文件打包進(jìn)Assets中程序啟動(dòng)時(shí)加載。實(shí)測(cè)下來在Android 8以上版本的中文顯示效果非常干凈利落。如果你更看重字節(jié)數(shù)控制也可以繼續(xù)使用系統(tǒng)默認(rèn)字體但在部分國(guó)產(chǎn)ROM上會(huì)出現(xiàn)某些控件字體不一致的問題。注意務(wù)必在Application.Initialize之后、MainForm創(chuàng)建之前調(diào)用全局字體設(shè)置否則會(huì)有部分控件已經(jīng)初始化了錯(cuò)誤的字體引用導(dǎo)致后續(xù)切換不徹底。4.2 Windows縮放DPI下的布局錯(cuò)亂Win10/Win11系統(tǒng)下FMX應(yīng)用在高DPI縮放下偶爾會(huì)出現(xiàn)布局錯(cuò)亂的現(xiàn)象TMS FMX UI Pack本身對(duì)高分屏適配做得不錯(cuò)但如果你混用了原生FMX控件和TMS控件就可能出現(xiàn)“比例不協(xié)調(diào)”的問題。排查思路是檢查主窗體的Font.Size是否設(shè)置得過大以及TMS控件是否顯式指定了Width/Height。在高DPI模式下控件的物理像素和邏輯像素比例會(huì)變化如果某些控件用固定像素硬編碼縮放時(shí)就會(huì)露餡。我通常的做法是對(duì)所有主界面控件采用AlignClient或AlignTop/Left配合Margins自適應(yīng)避免使用絕對(duì)定位。這一習(xí)慣在跨平臺(tái)部署時(shí)能省掉80%的布局調(diào)整工作。4.3 編譯包體積與啟動(dòng)性能優(yōu)化Full Source版本組件編譯進(jìn)EXE后包體一般會(huì)比編譯版大一些因?yàn)楹诵拇a是以靜態(tài)鏈接方式進(jìn)入可執(zhí)行文件的。對(duì)于Windows桌面端來說這不是問題但Android APK的包體膨脹就會(huì)影響用戶下載轉(zhuǎn)化率。我的優(yōu)化實(shí)踐是使用RTTI裁剪配合代碼混淆實(shí)際上TMS FMX UI Pack的組件單元只會(huì)在你引用到相應(yīng)單元時(shí)才被鏈接所以不必太過恐慌。但有一點(diǎn)警惕在DUnitX單元測(cè)試項(xiàng)目里如果調(diào)用了整個(gè)組件包的所有單元最后Build出來的測(cè)試APK可能會(huì)超過100MB。這種情況需要手動(dòng)隔離測(cè)試項(xiàng)目與主業(yè)務(wù)項(xiàng)目的單元引用范圍。5. 源碼級(jí)定制那些常規(guī)文檔不會(huì)告訴你的技巧5.1 如何安全地修改組件源碼前面提到過full source的一大價(jià)值就是可以深度定制。但這里我必須要給一個(gè)誠(chéng)懇的建議不要輕易改動(dòng)源碼包里的公共接口尤其是public和published方法簽名。一旦改動(dòng)后續(xù)升級(jí)組件版本時(shí)合并成本會(huì)非常痛苦。更穩(wěn)妥的做法是通過繼承來擴(kuò)展。比如TMS FMX Grid你完全可以寫一個(gè)TMyGrid繼承TMSFMXGrid在子類中重寫需要修改的virtual方法或者增加新的屬性和事件。這樣既滿足了定制需求又保住了源碼的純凈性未來還能順暢升級(jí)組件版本。我自己的組件庫(kù)中就長(zhǎng)期維護(hù)著基于TMS FMX Grid擴(kuò)展的TGridEx、基于TMS FMX Chart擴(kuò)展的TChartEx這些擴(kuò)展類沉淀了我對(duì)業(yè)務(wù)交互的理解和封裝復(fù)用到新項(xiàng)目時(shí)效率極高。5.2 用攔截器Interposer做快速補(bǔ)丁還有一種臨時(shí)性的修改技巧叫“攔截器類”——就是聲明一個(gè)與原始類同名的新類在單元引用順序上讓自己的類“覆蓋”原類。這種方法適合快速驗(yàn)證某個(gè)修改方案是否可行但因?yàn)轱L(fēng)險(xiǎn)較大不建議在生產(chǎn)環(huán)境長(zhǎng)期使用。我這邊的實(shí)踐是先用攔截器驗(yàn)證效果確認(rèn)改動(dòng)思路沒問題后再?zèng)Q定是否升級(jí)成繼承方案或者將改動(dòng)提交給TMS官方建議合并。保持克制和規(guī)范才能在升級(jí)浪潮中持續(xù)穩(wěn)定地使用組件。5.3 主題定制與視覺統(tǒng)一TMS FMX UI Pack本身支持通過TMS FMX Style進(jìn)行主題定制。Full Source的好處是你可以直接看到Style文件里綁定的每個(gè)資源名比如“grid_cell_normal”、 “chart_series1_fill”這樣的命名改起來一目了然。我花了一個(gè)下午把整套組件包的主題色統(tǒng)一成了公司品牌色——深藍(lán)#FFA500點(diǎn)綴。做法并不復(fù)雜用TMS提供的Style Editor在Tools菜單里可以找到加載默認(rèn)Style批量替換顏色值保存后通過TStyleManager.SetStyle全局應(yīng)用。關(guān)鍵點(diǎn)是替換顏色時(shí)要保持Opacity的一致否則會(huì)出現(xiàn)“顏色對(duì)了但看起來怪怪的”情況。5.4 與第三方FMX庫(kù)的共存策略現(xiàn)實(shí)項(xiàng)目里幾乎沒人只用TMS一家組件庫(kù)。你可能同時(shí)用了DevExpress的FMX控件、Grijjy的Foundation庫(kù)或者ZXing的掃碼組件。這些庫(kù)之間是否會(huì)發(fā)生沖突我實(shí)測(cè)下來TMS FMX UI Pack的類名前綴大多是TMSFMX開頭命名空間隔離做得還算清晰沖突概率很低。唯一需要注意的是單元引用順序。如果兩個(gè)庫(kù)都定義了同名的輔助函數(shù)或常量鏈接時(shí)可能出現(xiàn)“Ambiguous overloaded call”的編譯錯(cuò)誤。解決方法是細(xì)化uses列表盡量按需引用避免在公共單元中粗暴地寫“uses TMSFMXGrid, MyUtils;”這種大雜燴。6. 總結(jié)與個(gè)人心得兜兜轉(zhuǎn)轉(zhuǎn)用了幾年TMS FMX UI Pack v3.7.7.5在我的工具箱里一直保持著比較高的存在感。它讓我在FMX框架下能做出讓客戶滿意的界面同時(shí)又不犧牲德爾菲開發(fā)者的高效率節(jié)奏。跨平臺(tái)不只是把代碼從一個(gè)平臺(tái)搬到另一個(gè)平臺(tái)更重要的是讓UI、交互和性能在每個(gè)平臺(tái)上都不過時(shí)、不別扭。最后分享一個(gè)我個(gè)人的工作習(xí)慣每接手一個(gè)新版本TMS組件包我先不看Release Notes而是打開Demo源碼逐個(gè)運(yùn)行官方示例再對(duì)照源碼看實(shí)現(xiàn)方式。這個(gè)過程往往比任何文檔都更快地讓我掌握組件的最佳實(shí)踐。v3.7.7.5的Demo工程里Grid和Chart相關(guān)的示例代碼尤其精彩有些高級(jí)用法至今我仍在翻閱。如果你正在FMX跨平臺(tái)項(xiàng)目里和數(shù)據(jù)表格、圖表、日期選擇做斗爭(zhēng)這套組件值得你花一個(gè)周末去試一下。在真正投入業(yè)務(wù)之前把環(huán)境裝好、把示例跑通、把源碼結(jié)構(gòu)摸清后面的一切都會(huì)順不少。本文還有配套的精品資源點(diǎn)擊獲取