Minn suġġett tar-rivista għall-prattika tal-proġett
Paġni ta' servizz u paġni tekniċi relevanti għall-artiklu
Min jidħol f‘Delphi FireMonkey biex jimmaniġġja diversi formfatti, isib ruħu malajr f‘Responsive Layouts FMX – u kważi immedjatament f’miżgħa ta‘ kaskati ta‘ Align, contenituri tal-layout moħbija u workaround tal-designer li jinqasmu fil-bidla li jmiss ta‘ DPI jew rotazzjoni. F’kliens ta‘ software ta‘ intrapriża li żviluppa maż-żmien dan huwa partikolarment inkwetanti: il-UI tiġi żviluppata, it-timijiet jinbidlu, u f’daqqa waħda l-logika tkun fuq dettalji viżivi.
Il-qalba tal-problema: FireMonkey jipprovdi bosta baċiri (eż. Align, Anchors, TScaledLayout, TFlowLayout, TGridPanelLayout), imma m’hemm l-ebda sistema ta‘ Breakpoints “nativa” bħalma hemm fil-web. Tista‘ tirreaġixxi għall-bidliet fid-daqs, imma mingħajr arkitettura ċara dan jispiċċa f’“if Width < … then …” distribwit fuq ħafna forms.
Dan l-artiklu juri Layout-Router: komponent żgħir li jimmaniġġja breakpoints b’mod ċentrali u jeħoddu Controls (jew blokki sħaħ tal-layout) bejn Slots ippreparati. L-għan: l-istati jinżammu, il-kodiċi jkun manutenzjonabbli, u każi fringe bħar-rotazzjoni, layouts imxerrda u re-entrancy jiġu mitgewati. Hemm ukoll ftit trikks inqas ovvji li fil-prattika jagħmlu d-differenza bejn “jopera fid-demo” u “jopera b’mod stabbli fil-produzzjoni”.
Għaliex Breakpoints f’FMX huma differenti mill-web
Fil-layouts tal-web il-breakpoints huma spiss deklarattivi (CSS Media Queries). F’FMX id-deċiżjonijiet tal-layout jiċċirkolaw tipikament imperattivament fit-tul tal-ħin: fil-OnResize isir il-bidla. Għal dan jżidu l-karatteristiċi speċifiċi tal-pjattaforma:
- Device-Pixel vs. logische Pixel:
ClientWidth/ClientHeighthuma f’unitajiet loġiċi (skont is-skalazzjoni). Bidliet fil-DPI (eż. Windows Per-Monitor-DPI) jistgħu jikkawżaw it-twessigħ mill-ġdid tal-layouts anke meta xejn ma nbidel “fisikament”. - Rotazzjoni u Safe Areas: Pjattaformi mobili jipprovdu insets (Notch/Safe Area) – skont l-OS u d-device. “Breakpoint biss skont il-wisa’” spiss għandu viżjoni ristretta, għax l-ispazju utilisabbli jista‘ jkun iżgħar mid-daqs totali tal-finestra.
- Pass tal-Layout: FireMonkey jikkalkula l-layouts f’fażijiet. Jekk jinbdel Parent/Align fil-mument mhux xieraq, jinqalgħu effett sekondarju (eż. ripetut reflow jew tibdil kontinwu fid-daqs li juri ‘flicker’).
Layout-Router jindirizza dan billi (1) jeviti l-„meta“ (Resize/Scale/Rotation) mill-„kif“ (regoli tal-layout) u (2) jikkonċentra r-regoli f’post wieħed. Għal mexxejja tekniċi l-iktar effett importanti hu: ikollhom ċentru ta‘ deċiżjoni ċar u eżaminabbli minflok ħafna każijiet speċjali lokali.
Arkitettura: Layout-Router ma‘ Slots minflok il-ħolqien ta‘ Controls
It-trick nadif għal FMX: mhux terġa‘ toħloq Controls b’mod dinamiku, imma terfa‘ controlli eżistenti bejn Slots. Slot huwa sempliċement container (eż. TLayout) li jirrappreżenta żona tal-UI: kolonna laterali, bar tal-għodda, kontenut, footer, panel tad-dettalji.
Vantaġġi f’software ta‘ intrapriża personalizzata:
- L-istati jinżammu (Edit-Text, posizzjoni tal-iskroll, oġġetti magħżula), għax l-istanzi ma jerġgħux jinħolqu mill-ġdid.
- Inqas riskju ta‘ doppja konnessjoni ta‘ events, timers jew bindings.
- Ir-regoli tal-layout isiru viżibbli: “liema blokk jinsab f’liema slot” jista‘ jiġi segwit u reviżjonat għal kull breakpoint.
Importanti għall-prattika: Aqta‘ l-blokki tal-UI biżżejjed b’mod ġenerali. Jekk tgħawweġ 30 kontroll individwali, il-lista tar-rotta stess saret sors ta‘ żbalji. Aħjar huma kontenituri bħal layFilterBar, layNavigation, layResultList, layDetails.
Snippet tas-sors: Breakpoint-Router għal Responsive Layouts FMX
Il-kodiċi li ġej huwa maħsub bħala unità ta‘ għajnuna li tista‘ tuża f’FMX-Forms. Huwa jikkalkula breakpoint (XS/SM/MD/LG/XL) u jpoġġi kontrolli definiti fi kontenituri ta‘ slot speċifiċi. Dettalji importanti:
- Debounce permezz ta‘
TThread.ForceQueue: diversi eventi ta‘ Resize jiġu mħallta f’aġġornament wieħed (inqas jitters tal-UI, inqas ċikli ta‘ reflow). - Protezzjoni kontra r-re-entrancy: l-aġġornament tal-layout spiss jikkawża l-ħruġ mill-ġdid ta‘ eventi Resize/Layout.
- Optionali: Orientazzjoni (Portrait/Landscape) tista‘ tidħol fil-loġika tal-breakpoint.
unit NB.FMX.LayoutRouter;
interface
uses
System.Classes, System.SysUtils, System.Types, System.Generics.Collections,
FMX.Types, FMX.Controls;
type
TNBLayoutBreakpoint = (bpXS, bpSM, bpMD, bpLG, bpXL);
// Mappa: liema Control għandu jittieħed f'liema Slot (container) għal kull Breakpoint.
TNBRoute = record
Control: TControl;
TargetSlot: TControl; // tipikament TLayout jew TPresentedControl
Align: TAlignLayout;
end;
TNBRouteList = TList<TNBRoute>;
TNBGetBreakpointEvent = reference to function(const AClientSize: TSizeF): TNBLayoutBreakpoint;
TNBLayoutRouter = class(TComponent)
private
FRoot: TControl;
FPending: Boolean;
FUpdating: Boolean;
FCurrent: TNBLayoutBreakpoint;
FOnGetBreakpoint: TNBGetBreakpointEvent;
FRoutes: TObjectDictionary<Integer, TNBRouteList>;
function KeyOf(const ABp: TNBLayoutBreakpoint): Integer;
procedure RootResized(Sender: TObject);
procedure ApplyPending;
procedure ApplyRoutes(const ABp: TNBLayoutBreakpoint);
function DefaultBreakpoint(const AClientSize: TSizeF): TNBLayoutBreakpoint;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure AttachRoot(const ARoot: TControl);
procedure DefineRoute(const ABp: TNBLayoutBreakpoint; const AControl, ASlot: TControl;
const AAlign: TAlignLayout = TAlignLayout.Client);
procedure Invalidate; // iġġedded manwalment
property Current: TNBLayoutBreakpoint read FCurrent;
property OnGetBreakpoint: TNBGetBreakpointEvent read FOnGetBreakpoint write FOnGetBreakpoint;
end;
implementation
{ TNBLayoutRouter }
constructor TNBLayoutRouter.Create(AOwner: TComponent);
begin
inherited;
FRoutes := TObjectDictionary<Integer, TNBRouteList>.Create([doOwnsValues]);
FCurrent := bpMD;
end;
destructor TNBLayoutRouter.Destroy;
begin
if Assigned(FRoot) then
FRoot.OnResize := nil;
FRoutes.Free;
inherited;
end;
procedure TNBLayoutRouter.AttachRoot(const ARoot: TControl);
begin
if FRoot = ARoot then
Exit;
if Assigned(FRoot) then
FRoot.OnResize := nil;
FRoot := ARoot;
if Assigned(FRoot) then
FRoot.OnResize := RootResized;
Invalidate;
end;
procedure TNBLayoutRouter.DefineRoute(const ABp: TNBLayoutBreakpoint; const AControl,
ASlot: TControl; const AAlign: TAlignLayout);
var
LKey: Integer;
LList: TNBRouteList;
LRoute: TNBRoute;
begin
if (AControl = nil) or (ASlot = nil) then
raise EArgumentNilException.Create('Control/Slot ma jistgħux ikunu nil');
LKey := KeyOf(ABp);
if not FRoutes.TryGetValue(LKey, LList) then
begin
LList := TNBRouteList.Create;
FRoutes.Add(LKey, LList);
end;
LRoute.Control := AControl;
LRoute.TargetSlot := ASlot;
LRoute.Align := AAlign;
LList.Add(LRoute);
end;
function TNBLayoutRouter.KeyOf(const ABp: TNBLayoutBreakpoint): Integer;
begin
Result := Ord(ABp);
end;
procedure TNBLayoutRouter.RootResized(Sender: TObject);
begin
Invalidate;
end;
procedure TNBLayoutRouter.Invalidate;
begin
if (FRoot = nil) or FUpdating then
Exit;
// Debounce: applikat darba biss kull ċiklu tal-messaġġi
if FPending then
Exit;
FPending := True;
TThread.ForceQueue(nil,
procedure
begin
ApplyPending;
end);
end;
procedure TNBLayoutRouter.ApplyPending;
var
LBp: TNBLayoutBreakpoint;
LSize: TSizeF;
begin
if (FRoot = nil) then
Exit;
if not FPending then
Exit;
FPending := False;
LSize := TSizeF.Create(FRoot.Width, FRoot.Height);
if Assigned(FOnGetBreakpoint) then
LBp := FOnGetBreakpoint(LSize)
else
LBp := DefaultBreakpoint(LSize);
if LBp = FCurrent then
Exit;
ApplyRoutes(LBp);
FCurrent := LBp;
end;
procedure TNBLayoutRouter.ApplyRoutes(const ABp: TNBLayoutBreakpoint);
var
LList: TNBRouteList;
LRoute: TNBRoute;
begin
if FUpdating then
Exit;
FUpdating := True;
try
if not FRoutes.TryGetValue(KeyOf(ABp), LList) then
Exit;
// Attenzjoni: bidla fil-Parent tibdel il-Z-Order.
// Jekk il-ordni hija rilevanti, sejjaħ DefineRoute fil-ordni mixtieqa.
for LRoute in LList do
begin
if (LRoute.Control.Parent <> LRoute.TargetSlot) then
LRoute.Control.Parent := LRoute.TargetSlot;
// L-Align għandu jitqiegħed biss wara li jiġu stabbiliti l-Parent, inkella l-Bounds jistgħu jiġu interpretati differenti.
LRoute.Control.Align := LRoute.Align;
LRoute.Control.Visible := True;
end;
finally
FUpdating := False;
end;
end;
function TNBLayoutRouter.DefaultBreakpoint(const AClientSize: TSizeF): TNBLayoutBreakpoint;
var
W: Single;
begin
W := AClientSize.cx;
// Breakpoints intenzjonalment ġeneralizzati, għax il-pjattaformi ta' FMX jvarjaw b'mod sinifikanti.
if W < 480 then Exit(bpXS);
if W < 768 then Exit(bpSM);
if W < 1024 then Exit(bpMD);
if W < 1440 then Exit(bpLG);
Result := bpXL;
end;
end.
Kif tuża r-Router f’Form
Tiddedefinixxi slots bħala TLayout (eż. layTop, layLeft, layContent) u mbagħad tirreġistra għal kull Breakpoint fejn jinsabu l-blokki. B’mod tipiku Sidebar u Details-Pane f’breakpoints żgħar jiċċaqilqu waħda taħt l-oħra.
procedure TFrmMain.FormCreate(Sender: TObject);
begin
FRouter := TNBLayoutRouter.Create(Self);
FRouter.AttachRoot(LayoutRoot); // z. B. ein TLayout, das Client-aligned ist
// XS: alles untereinander
FRouter.DefineRoute(bpXS, layToolbar, slotTop, TAlignLayout.Top);
FRouter.DefineRoute(bpXS, laySidebar, slotContent, TAlignLayout.Top);
FRouter.DefineRoute(bpXS, layDetails, slotContent, TAlignLayout.Top);
FRouter.DefineRoute(bpXS, layMain, slotContent, TAlignLayout.Client);
// MD: Sidebar links, Details rechts
FRouter.DefineRoute(bpMD, layToolbar, slotTop, TAlignLayout.Top);
FRouter.DefineRoute(bpMD, laySidebar, slotLeft, TAlignLayout.Client);
FRouter.DefineRoute(bpMD, layMain, slotCenter, TAlignLayout.Client);
FRouter.DefineRoute(bpMD, layDetails, slotRight, TAlignLayout.Client);
// Optional: eigene Breakpoint-Logik
FRouter.OnGetBreakpoint :=
function(const S: TSizeF): TNBLayoutBreakpoint
begin
if (S.cx < 700) or (S.cy < 420) then
Result := bpSM
else
Result := bpMD;
end;
end;Kuntest: Waarom „Umhängen“ spiss ikun aktar stabbli minn Visible-Schalten
Ħsieb komuni hu li żżomm siġar tal-layout separati għal kull varjant u biss tagħmel toggle lil Visible. Dan jidher komdu fid-designer, iżda għandu effetti sekondarji tipici:
- Binding/Events doppju: Żewġ controls simili jeħtieġ li jinżammu sinkronizzati (eż. żewġ barra ta‘ filtru).
- Ordni tat-Tab u Fokus: Meta tinbidel, titilfu l-fokus jew tasal f’controls moħbija jekk
TabStop/HitTestmhux f’kundizzjoni favorevoli. - State Drift: Pożizzjonijiet ta‘ scroll, stati ta‘ selezzjoni jew test editjat jistgħu jiddiferenzjaw.
It-„Umhängen“ iżomm l-istanza distint. Huwa importanti li taqta‘ l-blokki tal-layout b’mod li jistgħu jitpoġġew indipendentement (pereż., „Sidebar“ bħala container separat minflok bosta controls individwali). Dan iħallas ruhu fil-manutenzjoni u fl-analiżi tal-iżbalji: inti tidentifika u tissolva żbalji fuq istanza waħda, mhux fuq żewġ UIs skuri paralleli.
Stolperfallen in der Praxis (und wie man sie debuggt)
1) Resize-Stürme und Re-Entrancy
FMX jattiva OnResize mhux biss waqt user-resize, iżda wkoll waqt bidliet ta‘ style, bidliet tal-parent u xi drabi bidliet fil-DPI. Mingħajr mekkanizmu ta‘ debounce l-app tista‘ tinżamm f’loops ta‘ layout. Ir-router juża TThread.ForceQueue biex joffri l-bidliet fil-UI-tick li jmiss.
Parir għall-debugging: logging (pereż. permezz ta‘ OutputDebugString) b’Breakpoint, daqs u kontatur ta‘ aġġornamenti jgħin biex jinstabu loopijiet ta‘ reflow. Jekk teżegwixxi wkoll log tal-ħin meta ApplyRoutes tibda u tispiċċa, tara malajr jekk resize wieħed qed jikkawża kaskata.
2) Z-Order, HitTest und „unsichtbare“ Klick-Blocker
Bidliet tal-parent jibdlu l-Z-Order. Jekk overlays (pereż., Flyouts) ma jġibu aktar klikks, spiss huwa minħabba li container client-aligned jinsab fuqhom u HitTest huwa attiv. Varjanti: għall-oqsma ta‘ overlay ipproġetta slot separat fuq nett u parentja biss dawk il-controls hemmhekk. Fil-FMX, HitTest (jekk control jinterċetta eventi tal-maws/touch) spiss ikun il-kawża aktar milli l-viżibilità.
3) TGridPanelLayout und prozentuale Größen
TGridPanelLayout jista‘ jħalli reberekalkuli mhux mistennija meta jintuża ma‘ kolonni/linji procentwali flimkien ma‘ Align=Client u b’ċirkostanzi ta‘ reparenting dinamiku. Jekk trid tuża Grid, poġġih f’slot u imbagħad ibiddlu biss blokki sħaħ tal-Grid, mhux it-tfal tal-Grid. Dan jnaqqas il-kombinatorika tal-passi tal-layout.
4) Fokus, tastiera virtwali u kampjiet ta‘ input li „jitilgħu“
Każ limiti li jseħħ f’apps FMX fuq mobbli u anke fuq Windows-tablets: waqt ir-reparenting control ta‘ edit li għandu fokus jista‘ jitlef il-parent temporanjament. Dan jista‘ jagħlaq it-tastiera virtwali jew issettja mill-ġdid il-kursor. Prattiċi li ħadmu tajjeb: qabel il-routing issalva temporanjament il-fokus attwali (Focused/IFMXFocusControl), u wara r-routing (fl-istess UI-tick) irrestawralu. Dan huwa partikolarment utli f’formoli tal-input li jibdlu bejn “żewġ kolonni” (Tablet/PC) u “kolonna waħda” (Phone).
Varjanti: Breakpoints skont il-form factor minflok biss skont il-wisa‘
F’clients multiplatform reali, il-„wisa'“ waħda biss spiss mhix is-sinjal korrett. Varjanti sensibbli:
- Wisa‘ u għoli: twieqi ħafna rqiqa (eż. terminals tal-kassa, skrins maqsuma) jeħtieġu regoli differenti.
- Oriëntazzjoni:
Landscapefuq tablets spiss huwa simili għall-desktop, while Portrait huwa aktar simili għall-mobile. - Zona utilisabbli tal-safe-area: fuq iOS/Android l-għoli effettiv disponibbli jista‘ jonqos b’mod sinifikanti minħabba l-barijiet tas-sistema. Min jikkonsidra biss
Heightkultant jirruta ‚tard‘.
Il-router huwa maħdum b’mod deliberat sabiex tkun tista‘ tbiddel il-funzjoni tal-breakpoint. Dan huwa utli wkoll f’sitwazzjonijiet legacy, meta l-istess form tkun qed tħaddem f’hosts multipli (eż., darba bħala tieqa normali, darba f’kontenitur imqiegħed ġewwa).
Straordinarjament nadif: Layout-Routing bħala „Transazzjoni”
F’screens kbar il-problema spiss ma tinsabx biss fil-breakpoints, iżda fl-ordni tal-operazzjonijiet tal-UI. Mudell prattiku huwa li trattah il-routing bħala Transazzjoni: l-ewwel iddeċiedi, imbagħad tagħmel ir-reparenting, u mbagħad twettaq l-effetti sekondarji (viżibilità, fokus, rifreš tad-dejta) b’ordni.
B’mod konkrett: evita li controls individwali waqt ir-reparenting joħolqu events proprji li minnhom jinbdew operazzjonijiet ta‘ layout jew aċċess tad-data. F’FMX dan jista‘ jiġri, pereżempju, meta waqt il-bidla tal-parent jiġu sparati OnEnter/OnExit jew meta espressjoni ta‘ LiveBinding tiġi r-evalwata mill-ġdid minħabba bounds-update. Jekk tara effetti bħal dawn, jgħin swiċċ ċentrali „Updating“ (bħal dak fil-router) flimkien ma‘ pass post ċar: biss wara ApplyRoutes għandhom jitħaddmu operazzjonijiet kostużi (eż., terġa‘ tinżamm il-lista, tirrebindja d-dettalji).
Dan għandu importanza speċjali fuq clients b’aċċess REST: reload mhux mixtieq waqt resize jista‘ jwassal għal requests bla bżonn. Dan mhux innota f’LAN, imma jidher immedjatament fuq VPN jew fuq netwerks mobbli.
Meta l-approċċ jiswa – u fejn għandu limiti
Ir-router tal-layout jiswa jekk:
- applikazzjoni FMX tibqa‘ tintuża għal snin u diversi żviluppaturi jaħdmu fuq l-istess screens,
- blokkijiet tal-UI jistgħu jiġu separati b’mod ċar (Sidebar/Details/Content),
- jeħtieġu regoli ta‘ breakpoint riproduċibbli minflok tuning ad-hoc ta‘ Align.
Tista‘ tara limitazzjonijiet meta skrin għandu jkun ħafna “fluid” (ħafna kaxxi dinamici, Masonry-Layouts reali). F’dawk il-każijiet TFlowLayout/TGridPanelLayout jew klassijiet ta‘ layout proprji huma aktar xierqa. Anki meta ħafna kontrolli individwali jgħaddu bejn slots, il-manutenzjoni tar-routi ssir mhux ċara – f’dak il-punt aħjar taqta‘ blokki ikbar jew tuża saff ta‘ konfigurazzjoni deklarattiva (eż. konfigurazzjoni JSON għall-asignazzjonijiet tal-slots li titla‘ waqt l-avviż).
Konklużjoni: Għall-Responsive Layouts f’FMX, “tqabbil mill-ġdid bl-użu ta‘ Breakpoints” huwa mezz pragmatiku: inqas kaos tad-disinn, regoli ċari, stati stabbli. Ma jemendax struttura tal-UI mħejjija sew, imma jagħtik qafas affidabbli biex tiżviluppa b’mod kontrollat il-klijenti FMX fi soluzzjonijiet korporattivi diġitali maż-żmien u fuq diversi formfactors.
Jekk f’applikazzjoni eżistenti Delphi jew FMX trid timplementa b’mod nadif arkitettura tal-layout bħal din mingħajr ma tirriskja regressjonijiet tal-UI f’ssenarji operattivi, tista‘ tiddefinixxi dan teknika magħna: iddiskutew proġett jew inizjattiva ta‘ modernizzazzjoni ma‘ Net-Base.
Fil-kuntest professjonali, Delphi Fmx Breakpoints u Firemonkey Layout jilagħbu wkoll rwol importanti meta integrazzjonijiet, flussi tad-data u żvilupp kontinwu jeħtieġu jilagħbu tajjeb flimkien.
Iddiskuti proġett jew inizjattiva ta‘ modernizzazzjoni ma‘ Net-Base.
Pass li jmiss
Meta suġġett jissarraf f’proġett reali, l-arkitettura, is-sistema eżistenti u l-operazzjoni għandhom jiġu kkunsidrati flimkien kmien.
Aħna nappoġġjaw mhux biss f'kwistjonijiet puntwali, iżda wkoll meta biċċiet ta' kodiċi sors, temi legacy jew ideat għal portali jridu jsiru proġett korporattiv stabbli u affidabbli.
- L-istat attwali, l-istat tal-mira u r-riskji tekniċi jiġu vvalutati flimkien.
- REST, aċċess għad-dejta, portali u Rollout mhux se jiġu posposti bħala konsegwenzi tardivi.
- Tara kmieni liema triq hija sostenibbli kemm mill‑punt ta’ vista ekonomiku kif ukoll mill‑punt ta’ vista operattiv.