mercoledì 11 giugno 2014

DelphiDay Italia: buongiorno! App per iOS e Android!

Ci siamo, è il gran giorno! :-)

Buongiorno a tutti i partecipanti del DelphiDay Italia 2014!

Con un tempismo perfetto (ehm...), Apple ha approvato la nostra App (già disponibile da qualche giorno su Google Play Store per i dispositivi Android)!



Ecco i link per scaricarla (vi offre il programma della giornata, i riferimenti dei nostri sponsors e la possibilità di valutare le sessioni, in forma anonima!).


Ci vediamo fra poco ! :-)

Andrea

mercoledì 4 giugno 2014

DelphiDay Italia: iscriviti oggi! E scarica la nostra App!

Oggi è l'ultimo giorno per iscriversi al prossimo Delphi Day Italia!

L'evento, organizzato da Wintech-Italia si terrà mercoledì 11 giugno a Piacenza e rappresenta il principale evento di incontro per i programmatori Delphi in Italia. Il giorno precedente sono in programma dei seminari su argomenti specifici.

Vi rimando, per tutti i dettagli al sito http://www.delphiday.it e vi segnalo che la nostra applicazione mobile è disponibile sul Play Store di Google.


Scaricatela per avere sempre a disposizione il programma della giornata e potrete valutare le sessioni secondo il vostro gradimento (in modo del tutto anonimo :-).

venerdì 30 maggio 2014

Personalizzare il Text di una TListView con MultiDetailAppearance

Durante lo sviluppo di una applicazione FireMonkey mobile è quasi impossibile non trovarsi nella situazione di voler personalizzare l'aspetto grafico di una TListView.

Questo accade generalmente perchè si vogliono mostrare più dati relativi ad un singolo Item, rispetto ai casi più semplici (dove basta impostare Text e magari DetailText).

Uno degli esempi forniti (già da XE4) da Embarcadero (c'è un articolo di Sarina DuPont a riguardo), mostra come sia possibile customizzare l'aspetto degli elementi di una TListView in modo da avere a disposizione più "DetailText", da collegare ad esempio attraverso l'uso dei LiveBindings alla nostra fonte dati (un dataset, per esempio).

Per chi usa RAD Studio XE6, l'esempio si trova nella cartella:

C:\Users\Public\Documents\Embarcadero\Studio\14.0\Samples\Object Pascal\Mobile Samples\User Interface\ListView

L'esempio è completo di un package SampleListViewMultiDetailAppearancePackage che registra nel vostro IDE una nuova ItemAppearance da applicare alla vostra ListView.



Manipolando le proprietà e gli oggetti del nodo ItemAppearance, è possibile personalizzare moltissimi aspetti del nostro TListViewItem, ad esempio il font (dimensione, colore, stili) e il posizionamento del testo (per ogni singolo Detail, usando allineamenti e tutte le altre opzioni come Opacity e WordWrap...).

Per esempio, possiamo dire che vogliamo colorare di rosso il primo Detail di ogni Item:



Forse per una svista, non è presente il nodo Text (al livello dei nodi MultiDetail1/2/3) e quindi non sembra possibile manipolare in alcun modo il testo principale degli item...

In realtà con una piccola modifica alla unit MultiDetailAppearanceU.pas e cioè semplicemente ripubblicando la proprietà Text della classe TMultiDetailItemAppearance e ricompilando il package SampleListViewMultiDetailAppearancePackage, otteniamo nuovamente il nodo Text (presente nelle ItemAppearance standard):

La modifica da effettuare: ripubblicare la proprietà Text di TMultiDetailItemAppearance

Il nodo Text è così visibile (dopo aver ricompilato il package) nell'Object Inspector

Diventerà così facilissimo modificare comodamente nell'IDE tutte le opzioni di Text e ottenere un aspetto personalizzato del testo principale degli item della TListView (con MultiDetailAppearance).

Ad esempio ho impostato che il Text fosse verde e grassetto:


Spero possa esservi utile!

A presto,
Andrea

lunedì 12 maggio 2014

DelphiDay Italia 2014: 30 giorni all'evento!

Mancano 30 giorni all'appuntamento annuale per gli sviluppatori Delphi in Italia: il DelphiDay 2014 (tredicesima edizione) che si terrà, come di consueto, a Piacenza (presso il Best Western Park Hotel), mercoledì 11 Giugno!


Una giornata, organizzata da Wintech-Italia, dedicata alla comunità Delphi in Italia, con diverse sessioni sulle ultime novità della versione XE6 ed interessanti occasioni di scambio fra i partecipanti nelle sessioni pomeridiane (tavole rotonde), dove è previsto un alto livello di coinvolgimento dei presenti.



Le iscrizioni sono aperte ! 
(Termine: 04 Giugno)
Il costo dell'iscrizione alla conferenza (40 €) comprende il pranzo e il coffee break.

Ospiti internazionali:
  • Marco Cantù (Embarcadero, Delphi and RAD Studio Product Manager)
    • ci aggiornerà sullo stato di Delphi, sulle ultime novità e su cosa ci aspetta
    • è previsto uno spazio durante la conferenza per permettere a i partecipanti di fare qualche domanda al Product Manager (preparatevi :-) ) 
  • Michael Philippenko (Fast Reports, CEO e co-fondatore)
    • insieme ad un collega sviluppatore, ci mostrerà l'ultima versione di Fast Reports e le novità in ambito FireMonkey del motore di reportistica di Delphi.
Gold Sponsors (che ringraziamo):
Anche quest'anno, il giorno precedente alla conferenza (martedì 10 Giugno), è prevista una giornata di seminari rivolti agli sviluppatori Delphi. Gli argomenti di questa edizione sono i seguenti:
  • FireDAC 
  • Delphi REST Client Library 
  • BaaS con Delphi XE6 
  • Costruire una API REST
Ogni sessione è di mezza giornata e ogni partecipante può scegliere quali sessioni seguire (sia la mattina che il pomeriggio), componendo così la giornata in base alle proprie esigenze.
I seminari saranno tenuti da me e da Paolo Rossi, per Wintech-Italia.
Tutti i dettagli sul contenuto dei seminari alla pagina: http://www.delphiday.it/seminari.html

E' previsto uno sconto se vi iscrivete sia ai seminari che alla conferenza!

Vi segnaliamo inoltre l'Evento Gestionale Open che invece segue la conferenza (giovedì 12 Giugno): l'evento, di mezza giornata, è gratuito e propone quattro soluzioni di integrazione con il gestionale.


Per dettagli e iscrizioni, visitate il sito www.delphiday.it !


mercoledì 2 aprile 2014

Un menù dinamico per app mobile (con Delphi XE5 per iOS e Android)

Introduzione

Uno dei problemi più comuni quando si sviluppa un'applicazione mobile è che, per implementare tutte le funzionalità richieste, abbiamo bisogno di più "schermate" (per esempio perchè dobbiamo visualizzare molti dati o controlli utente o perchè le funzionalità richiedono di essere suddivise in unità visuali isolate).

Al di là della scelta da compiere (valutando i pro e contro del caso) fra l'uso di TTabControl con più pagine rispetto all'opzione di realizzare una applicazione con più form, per tutti esiste il problema di guidare il proprio utente attraverso le varie "schermate".

Una soluzione molto diffusa è quella di implementare un drawer (un compartimento laterale a scomparsa che mostra un elenco di voci cliccabili). José Leon ha una serie di articoli a riguardo che sono molto interessanti.
Alternativamente, è possibile avere un pulsante che mostri un pannello a scomparsa più piccolo (simile ad un popup menu) e trovate un esempio di questo sempre sul blog di José Leon

Voglio proporvi anche un'altra alternativa, con relativa implementazione di base, che consiste in un menù (sempre a scomparsa: lo spazio è prezioso nelle app mobile) ma che abbia un po' di flessibilità sul numero di elementi cliccabili e che sia facile da manutenere a design-time.

Realizzazione

In breve, ho cercato di combinare la flessibilità (e le performance) di una TListView con il meccanismo di transizione a scomparsa utilizzato anche negli altri approcci che vi ho indicato poco sopra. Inoltre, per cercare di rendere più facile l'utilizzo e la manutenzione del menù a design-time, ho pensato di usufruire di una TActionList (comoda da maneggiare nell'IDE): lo sviluppatore in questo modo non deve fare altro che popolare l'ActionList con le voci che vorrà ritrovare nel menù a scomparsa.

Come potete vedere dal seguente screenshot, i componenti del MenuLayout (che rappresenta il menù nella sua interezza), sono sostanzialmente una TListView, un paio di animazioni, un MenuButton (in realtà un'istanza di TRectangle, colorato con un gradiente) che permette all'utente di avere un punto di "presa" del menù anche quando è chiuso e un effetto di ombra usato fini estetici.


A design-time, lo sviluppatore vede il menù "chiuso", nella parte superiore della form, riducendo così anche fastidiosi "ingombri" durante la fase di sviluppo del resto dell'applicazione:


Nota: il triangolo che si vede è realizzato usando un pezzetto di SVG e un componente TPath (questa funzionalità di FireMonkey mi piace molto e probabilmente sarà oggetto di un futuro blog post).

Grazie ad un utilizzo mirato delle proprietà Align, la MenuListView ha modo di crescere (in verticale) insieme al MenuLayout, mentre il MenuButton è allineato alBottom (in modo da essere sempre sul fondo inferiore del MenuLayout.

L'esecuzione (una tantum) di un pezzo di codice che aggiunge dinamicamente (a runtime) gli item alla TListView partendo dal contenuto della MenuActionList, ci porta in una situazione in cui per ogni action esiste un elemento della TListView. 
Dato che vogliamo preservare la piena funzionalità sia che si tratti di pochi elementi o di molti, sfruttiamo le funzionalità di scroll insite nella TListView per ottenere (senza sforzo) che, qualora gli elementi dovessero essere più di quelli che si possono mostrare fisicamente nello spazio disponibile, l'utente abbia facoltà di vederne solo alcuni ma poter fare scroll della ListView (come di consueto).

Due istanze di TFloatAnimation (MenuDownAnimation e MenuUpAnimation) sono state impostate per espandere il MenuLayout (agendo sulla sua proprietà Height) tanto quanto necessario per mostrare gli elementi del menù (con un effetto grafico piacevole e che potete ulteriormente customizzare modificando le opzioni delle animation stesse) e, viceversa, per collassarlo nuovamente. 

Questo è tutto ciò che ci serve dal punto di vista dei componenti visuali; per quanto riguarda il codice, vi riporto di seguito la porzione che si occupa di travasare le action in elementi della TListView:

procedure TMainForm.BuildMenuItems;
var
  LAction: TAction;
  LIndex: Integer;
  LItem: TListViewItem;
begin
  MenuListView.ItemAppearance.ItemHeight := 75;
  for LIndex := 0 to MenuActionList.ActionCount -1 do
  begin
    LAction := MenuActionList.Actions[LIndex] as TAction;

    LItem := MenuListView.Items.Add;
    try
      LItem.Text := LAction.Text;
      ItemActionDict.Add(LItem, LAction);
    except
      LItem.Free;
      raise;
    end;
  end;
end;

Come potete vedere è tutto abbastanza semplice e si nota l'utilizzo di ItemActionDict: una istanza di TDictionary<TListViewItem, TAction> che mi permette di realizzare una lista associativa fra gli elementi della TListView e le Action della TActionList.
Questa associazione servirà quando, nella gestione dell'evento di click dell'item della TListView, occorre sapere quale action eseguire:

procedure TMainForm.MenuListViewItemClick(const Sender: TObject;
  const AItem: TListViewItem);
var
  LItem: TListViewItem;
  LAction: TAction;
begin
  if ItemActionDict.TryGetValue(AItem, LAction) then
  begin
    if LAction.Execute then
      MenuUp;
  end;
end;

Il resto del codice è molto semplice e lo trovate nei sorgenti completi dell'applicazione di esempio che ho realizzato e di cui vedete alcuni screenshot di seguito (presi con un Nexus 7 e con un iPad):

 




 



Per ottenere un effetto di differenziazione rispetto al normale contenuto della form, ho personalizzato la MenuListView implementando un gestore per l'evento OnUpdatingObjects, che mi fornisce l'occasione di cambiare il colore del testo di ogni item della TListView;

procedure TMainForm.MenuListViewUpdatingObjects(const Sender: TObject;
  const AItem: TListViewItem; var AHandled: Boolean);
begin
  if Assigned(AItem) and Assigned(AItem.Objects) and Assigned(AItem.Objects.TextObject)
     and (AItem.Index < High(FItemColors))
  then
    AItem.Objects.TextObject.TextColor := FItemColors[AItem.Index];
end;

Inoltre, ho voluto personalizzare il colore di sfondo degli item della TListView, per averlo più scuro (differenziando quindi questa TListView dalle altre presenti nell'applicazione), implementando il metodo OnApplyStyleLookup e sfruttando un piccolo hack: modifico dinamicamente un valore dettato dallo style della TListView, in particolare il TColorObject di nome 'itembackground':

procedure TMainForm.MenuListViewApplyStyleLookup(Sender: TObject);
var
  LStyleObject: TFmxObject;
begin
  // hack the ListView style to have a dark background
  LStyleObject := MenuListView.FindStyleResource('itembackground');
  if LStyleObject is TColorObject then
    TColorObject(LStyleObject).Color := TAlphaColorRec.Slategray;
end;

Vi invito a provare ad aggiungere altre Action alla MenuActionList, tante da ottenere l'effetto di scroll nella TListView: il risultato è molto carino!

Conclusioni e materiale

Abbiamo visto come FireMonkey ci offra la possibilità di ottenere con facilità degli effetti grafici (animati) gradevoli e al contempo implementare una funzionalità fondamentale come può essere quella di un menù (con un numero arbitrario di voci) a scomparsa per una applicazione mobile.

Vi lascio anche qualche spunto per sviluppi ulteriori (più o meno facili, in ordine sparso):
  • espandere un po' gli effetti e le animazioni del menù, per migliorarne l'estetica e la user experience;
  • gestire lo stato di abilitazione/visibilità delle action corrispondenti gli item del menù;
  • aggiungere un supporto a icone da abbinare alle voci di menù;
  • migliorare la personalizzazione dei colori delle voci di menù;
  • rimuovere il MenuButton e al suo posto sfruttare delle gestures (o su Android, il pulsante Settings).
Vi lascio come di consueto i link al codice sorgente completo dell'applicazione demo realizzata e ad un'applicazione demo compilata per Android (APK).

Link: Codice sorgente Delphi XE5
Link: Demo APK (da installare sul vostro dispositivo Android)

Buon lavoro e a presto!

Andrea

lunedì 24 marzo 2014

Leggere e produrre Barcode con Delphi XE5 (Android)

UPDATENuovo blog post con la versione XE7

Introduzione

Esistono online diversi articoli che parlano di come integrare la lettura di codici a barre in applicazioni mobile Delphi (trovate dei link anche nel seguito di questo post).

Vedremo ora un approccio che permette di aggiungere le funzionalità di scansione e produzione di codici a barre (e/o QR code) in una app Delphi (Android), senza necessità di installare componenti di terze parti o compilare nella vostra app librerie di terzi.

Tutto si basa sull'interazione, mediata dal sistema operativo, con una applicazione già esistente (molto diffusa, gratuita e disponibile su Google Play Store) che si chiama Barcode Scanner (dettagli sulla licenza qui).

A costo di un certo livello di dipendenza da questa applicazione di terzi, il beneficio è di poterla sfruttare al massimo con poco sforzo, realizzando facilmente funzionalità che potrebbero essere sufficienti in un buon numero di situazioni.

Pro e contro

Vi anticipo subito quali sono i pregi/difetti di questo approccio:

Contro

  1. Si genera una dipendenza con Barcode Scanner (anche se solo per quanto riguarda le funzionalità di lettura/produzione di barcode, non per l'esecuzione in sè della vostra app): i vostri utenti dovranno installare sui loro dispositivi anche questa applicazione (attualmente gratuita);
  2. La funzionalità di lettura (scan) dei barcode si basa sull'assunto che Barcode Scanner di chiuda dopo aver scansionato un codice e copi il codice letto nella clipboard del dispositivo; se future versioni dell'applicazione dovessero comportarsi diversamente, la vostra applicazione potrebbe non funzionare;
  3. questa soluzione non è crossplatform (è valida solo per Android)

Pro

  1. Tutta la complessità di implementazione viene delegata a Barcode Scanner (incluso il supporto a numerosi formati di codici a barre e la gestione della camera);
  2.  Non è necessaria alcuna libreria di terze parti da compilare con la vostra applicazione;
  3. Sia la lettura che la produzione di codici a barre si risolve con una manciata di righe di codice!

Altri approcci e link di riferimento

Intent

Un Intent è sostanzialmente una descrizione di una operazione da compiere. Le applicazioni Android (activity) possono interagire attraverso gli intent (chiamandoli e registrandosi per servirli) un po' come le applicazioni Win32/64 possono interagire tramite ShellExecute o CreateProcess.
In Delphi sono presenti delle unit che ci aiutano a maneggiare direttamente i wrapper delle classi Java interessate e che ci permettono di effettuare le necessarie conversioni di tipo fra i tipi di dato Delphi e quelli Java. In particolare sono tre le unit che ci serviranno:

  1. Androidapi.JNI.GraphicsContentViewText, dove è definito il wrapper JIntent;
  2. Androidapi.JNI.JavaTypes, dove troviamo SharedActivity (una JActivity);
  3. FMX.Helpers.Android, che offre funzioni di utilità come StringToJString.
Una volta incluse queste unit, siamo in condizione di dichiarare ed eseguire un intent.
Come potete vedere in questo file Java di Barcode Scanner, essa espone diversi intent fra cui uno per avviare una scansione di un codice a barre e uno per produrre un codice a barre partendo da un contenuto testuale fornito nei parametri dell'intent.

Primo step: produzione di un codice a barre

Con queste poche righe di codice possiamo preparare ed eseguire l'intent ENCODE di Barcode Scanner:

procedure TForm1.ButtonProduceClick(Sender: TObject);
var
  Intent: JIntent;
begin
  Intent := TJIntent.JavaClass.init(StringToJString('com.google.zxing.client.android.ENCODE'));
  Intent.setPackage(StringToJString('com.google.zxing.client.android'));

  Intent.putExtra(StringToJString('ENCODE_TYPE'), StringToJString('TEXT_TYPE'));
  Intent.putExtra(StringToJString('ENCODE_FORMAT'), StringToJString(ComboBoxFormat.Items[ComboboxFormat.ItemIndex]));
  Intent.putExtra(StringToJString('ENCODE_DATA'), StringToJString(Memo1.Lines.Text));

  SharedActivityContext.startActivity(Intent);
end;


  • Il valore ENCODE_DATA rappresenta il contenuto che vogliamo rendere con il barcode (ed è il contenuto di un memo presente sulla form della nostra applicazione);
  • ENCODE_FORMAT rappresenta il tipo di codice a barre che vogliamo produrre (nel nostro esempio è preso da un combobox sulla form). I formati supportati attualmente sono i seguenti:
    "UPC_A", "UPC_E", "EAN_8", "EAN_13", "CODE_39", "CODE_93", "CODE_128", "ITF", "RSS_14", "RSS_EXPANDED", "QR_CODE", "DATA_MATRIX".
Il risultato è che una volta lanciata l'activity descritta dal nostro intent, l'applicazione Barcode Scanner verrà eseguita e riceverà i parametri che abbiamo impostato nell'intent, producendo per noi un barcode del tipo specificato e con il contenuto indicato.



Secondo step: lettura di un codice a barre

Sfruttando un secondo intent disponibile, possiamo chiedere a Barcode Scanner di avviarsi in modalità SCAN, presentando all'utente la tipica interfaccia grafica per individuare con la camera un codice a barre e scansionarlo. Una volta acquisito il codice, l'applicazione emette un beep acustico e si chiude, dopo aver copiato negli appunti il valore scansionato. Questo fa sì che la nostra applicazione torni in primo piano.

Il codice per eseguire l'intent SCAN è il seguente:

var
  Intent: JIntent;
begin
  // empty the clipboard (to be sure not to match previous results)
  FClipBoardService.SetClipboard('');
  WaitingForResults := True;

  // launch Barcode Scanner in SCAN mode
  Intent := TJIntent.JavaClass.init(StringToJString('com.google.zxing.client.android.SCAN'));
  Intent.setPackage(StringToJString('com.google.zxing.client.android'));

  Intent.putExtra(StringToJString('SCAN_MODE'), StringToJString('ONE_D_MODE,QR_CODE_MODE,PRODUCT_MODE,DATA_MATRIX_MODE'));

  SharedActivityContext.startActivity(Intent);
end;

In teoria, il modo corretto di intercettare la fine dell'attività di scansione sarebbe implementare un gestore per onActivityResult ma questo comporta attualmente alcune complicazioni tecniche.
Usiamo quindi un semplice workaround: intercettiamo l'evento che ci segnala che la nostra applicazione è di nuovo in primo piano (ottimo articolo di Pawel Glowacki) e leggiamo il contenuto degli appunti per catturare il testo corrispondente al barcode letto da Barcode Scanner.

Codice per agganciare gli eventi dell'applicazione e per ottenere un riferimento alla clipboard:
procedure TForm1.FormCreate(Sender: TObject);
begin
  FWaitingForResults := False;

  if not TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, IInterface(FClipBoardService)) then
    raise Exception.Create('Cannot get access to clipboard service!');

  if not TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(FApplicationEventService)) then
    raise Exception.Create('Cannot get application event service');
  FApplicationEventService.SetApplicationEventHandler(ApplicationEventHandler);
end;

Codice per l'event-handler dei cambi di stato dell'applicazione:
function TForm1.ApplicationEventHandler(AAppEvent: TApplicationEvent;
  AContext: TObject): Boolean;
var
  LClipboardContent: string;
  LFound: Boolean;
begin
  case AAppEvent of
    aeBecameActive:
      begin
        if WaitingForResults then
        begin
          WaitingForResults := False;
          LClipboardContent := FClipBoardService.GetClipboard.AsString;
          LFound := LClipboardContent <> '';

          if LFound then
            Log('Scan successful: ' + LClipboardContent)
          else
            Log('Scan failed (please retry)');
        end;
      end;
  end;
  Result := True;
end;

La procedura Log non fa altro che scrivere nel Memo della form principale.



Conclusioni e materiale

Questo è tutto. Come vedete si tratta di poche righe di codice e il meccanismo è semplice ma efficace.
Se le vostre esigenze di leggere e produrre barcode non sono particolarmente eccessive, un approccio come quello illustrato dovrebbe essere una soluzione sufficiente nella maggioranza dei casi.

Link: Codice sorgente Delphi XE5
Link: Demo APK (da installare sul vostro dispositivo Android)

UPDATE: Nuovo blog post con la versione XE7

Buon lavoro e a presto!
Andrea

venerdì 14 marzo 2014

90 giorni al Delphi Day 2014 (save the date!)

Mancano (meno di) novanta giorni al Delphi Day 2014, l'evento annuale che rappresenta il punto di incontro degli sviluppatori Delphi in Italia!




Giunto alla sua tredicesima edizione, è organizzato da Wintech-Italia e si terrà (come di consueto) a Piacenza il giorno 11 giugno 2014. Non potete mancare! :-)