lunedì 21 settembre 2015

TFrameStand: FireMonkey TFrame all'ennesima potenza!


Questo post è dedicato a TFrameStand: un componente per FireMonkey che ho sviluppato in questi ultimi mesi (nel mio tempo libero).

Prima di tutto, come reperirlo: il componente (e alcune demo) sono disponibili su GitHub e potete usarlo liberamente. Sull'ultimo numero (45-46) di Blaise Pascal Magazine trovate un mio articolo che spiega i fondamentali e illustra due delle demo incluse attualmente nel repository.

NB: gran parte del codice è stato sviluppato con XE8, ultimamente sto usando la versione 10 Seattle (quindi non è testato su versioni precedenti anche se dovrebbe essere compatibile anche con XE7).

Introduzione

Soprattutto durante lo sviluppo di applicazioni mobile, molti sviluppatori semplicemente aggiungono un sacco di controlli al form principale del progetto (che molte volte è anche l'unica form presente). Questo porta rapidamente a problemi di manutenzione del progetto, in primo luogo perché la form di solito diventa troppo affollata per essere modificata facilmente nel form designer e poi perché questo approccio non promuovere il riutilizzo degli elementi (codice e interfaccia utente) della vostra applicazione.

Il concetto di TFrame è stato introdotto molto tempo fa (anche nella VCL) per fornire allo sviluppatore la possibilità di legare insieme alcuni elementi dell'interfaccia utente (componenti) e alcuni comportamenti (codice) in un'unica unità, riutilizzabile. Questo è un bene perché è possibile suddividere l'applicazione in porzioni che possono essere progettate individualmente ed eventualmente riutilizzate (anche in altre applicazioni!) Semplicemente mettendo un'istanza di un TFrame su un oggetto contenitore (può essere una form, una pagina di un TTabControl o qualsiasi altro componente visuale FMX ...).

Un tipico esempio in cui un approccio a TFrame può semplificare molto è quando è possibile scomporre la vostra applicazione mobile in un insieme di "viste" da mostrare all'utente in base allo stato dell'applicazione stessa. È quindi possibile implementare le funzionalità applicative indipendentemente, mostrando questa o quella "vista" (TFrame) a seconda dello stato dell'applicazione stessa (come si farebbe con un TTabControl, usando una pagina diversa per ogni "vista").

TFrameStand nasce in questo contesto e permette di andare anche un po' oltre, dandovi l'opportunità di avere un livello intermedio tra il TFrame e l'oggetto contenitore. Questo strato intermedio (lo "stand", d'ora in poi) può essere visto come la porzione visuale (dell'interfaccia utente) che non è strettamente legata alle finalità del TFrame che si sta mostrando, ma è piuttosto legata al modo in cui il TFrame viene presentato all'utente. 
Questi "stand" sono definiti utilizzando una funzionalità FireMonkey molto basilare: gli stili! Ogni componente TFrameStand dispone di una proprietà StyleBook, un reference che può essere utilizzato per definire la raccolta di stand disponibili. Insieme ad un paio di semplici convenzioni di nomenclatura sono alla base del funzionamento del componente (ad esempio, per definire il punto esatto dove volete che il vostro TFrame sia incorporato nello stand occorre dare un nome preciso "container" al componente visuale che deve agire da parent del TFrame).

Stand e TFrames

Uno degli aspetti chiave di TFrameStand è che consente di riutilizzare uno stand con diverse classi TFrame discendenti e anche di fare l'opposto, cioè, mostrare la stessa classe TFrame usando diversi stand. In entrambi i casi, questo permette di ottenere un buon livello di  coerenza visuale nella vostra applicazione (e/o tra diverse applicazioni), a beneficio dell'esperienza utente.

Stesso stand, diversi TFrame

Per esempio, immaginate di avere uno stand che implementi un "modo" per mostrare un TFrame, ad esempio con un effetto LightBox: sarete quindi in grado di mostrare contenuti diversi (istanze TFrame diverse) utilizzando lo stesso effetto visivo (usando uno stand "lightbox"). I vantaggi sono evidenti, ad esempio, è necessario implementare l'effetto visivo (stand/supporto) solo una volta e poi si può riutilizzare e, così facendo, ogni cambiamento (miglioramento) realizzato sullo stand interesserà ogni parte della vostra applicazione che lo utilizza (coerenza!).

Stesso TFrame, diversi stand

Immaginate di avere una serie di stand che aiutino l'utente a riconoscere il contesto. Ad esempio, si può avere uno stand che indichi chiaramente che il contenuto è legato a una notifica di errore, un altro stand per le situazioni di attenzione (warning) e un altro ancora quando è richiesta l'azione dell'utente (conferme, scelte, e così via). Se, ad esempio, si dispone di un TFrame specifico per eseguire alcuni compiti e visualizzare i risultati per l'utente, è possibile che lo vogliate mostrare all'utente utilizzando diverse accezioni (stand diversi!). 
Tenete presente che, una volta implementati stand per ogni accezione, è possibile utilizzarli con qualsiasi TFrame (non sono richieste parentele o altre relazioni)  e che potete scegliere di volta in volta, quale accezione usare (e l'istanza TFrame ha un modo per capire con quale accezione è stato visualizzato, nel caso dovesse essere significativo).

In un'applicazione reale, molte situazioni ricadono in queste due categorie o in una via di mezzo. Molto dipende dal criterio scelto per scomporre la vostra applicazione usando i TFrame.

Funzionalità

Finora, abbiamo parlato di un paio dei vantaggi di questo approccio, che sono: scomporre la complessità di un'applicazione utilizzando dei TFrame e l'utilizzo di componenti TFrameStand per scomporre in qualche modo la modalità con cui il TFrame viene mostrato rispetto al contenuto specifico del TFrame stesso.

Ci sono alcuni altri benefici quando si utilizza TFrameStand, ecco un elenco dei principali:
  1. si può lavorare a design-time sia sul TFrame che sullo stand (o sugli stand);
  2. vi è un supporto integrato per eseguire un task in background (utilizzando la Parallel Programming Library internamente) e nel frattempo mostrare una opportuna interfaccia grafica (stand + TFrame) all'utente; una volta che l'operazione è stata completata, questa "form di attesa" può essere facilmente nascosta (con un po' di programmazione asincrona molto semplificata);
  3. TFrameStand è in grado di avviare automaticamente alcune animazioni (oggetti TAnimation e discendenti) contenute nella definizione dello stand o nell'istanza TFrame; Sono supportati attualmente due eventi: OnShow e OnHide (questo significa che è possibile utilizzare transizioni animate nel processo di mostrare e nascondere gli stand e i TFrame, facilmente e senza dover scrivere codice);
  4. vi è una (semplice) meccanismo di dependency injection che permette di ottenere facilmente un riferimento agli oggetti di contesto anche nel codice applicativo specifico di ogni TFrame da mostrare; questo significa che si è in grado, per esempio, di nascondere (o chiudere) lo stand da un pulsante sul TFrame incorporato o di farlo a livello di programmazione nel codice del TFrame;
  5. c'è un modo per rappresentare comportamenti condivisi (pezzi di codice, azioni) attraverso diverse combinazioni di stand / TFrame, per non duplicarle solo per via della necessità di utilizzare uno stand specifico o una classe TFrame specifica;
  6. c'è un modo (altamente personalizzabile) per sostituire la classe TFrame realmente utilizzata in fase di esecuzione; questo significa che è possibile sviluppare considerando una classe TFrame "progenitrice" e avere poi a runtime una istanza di una classe discendente, selezionata in base ad un algoritmo completamente personalizzabile (per esempio, si potrebbe decidere di utilizzare classi discendenti diverse a seconda della piattaforma d'esecuzione);
Scriverò altri blog post per illustrare queste funzionalità, ma se siete impazienti (e spero che lo siate) sentitevi liberi di dare un'occhiata ai progetti demo contenuti nel repository GitHub o almeno ai quattro piccoli video (gif animate, in realtà) in fondo a questo blog post. 
La maggior parte delle funzionalità principali sono coperti dalle demo e ne svilupperò altre a breve.

Infine, si può notare che un paio di demo sono legate al Material Design di Google per Android: usando TFrameStand è possibile (più facile) implementare alcuni elementi di Material Design nelle applicazioni FMX (nella demo ViewAndDialogs è implementata una transizione prendendo spunto dalle linee guida ufficiali nella documentazione Google), un argomento molto richiesto nel corso della mia attività di formatore e consulente per altri sviluppatori Delphi.

Ecco i "video" con le demo in esecuzione (su Win32):





Spero vi piaccia (e non esitate a esprimere il vostro parere!)

Andrea


3 commenti: