Visualizzazione post con etichetta Tips. Mostra tutti i post
Visualizzazione post con etichetta Tips. Mostra tutti i post

lunedì 1 dicembre 2014

Workaround per l'errore EBitmapLoadingFailed su Android Lollipop

Introduzione

C'è un bug di Delphi XE7 per cui le applicazioni Android non si avviano su Lollipop (Android 5.0). In particolare, vanno in crash subito dopo aver visualizzato lo splashscreen, a causa dell'eccezione EBitmapLoadingFailed che viene sollevata durante il caricamento delle immagini contenute nel file .fmx .
Lo stessa applicazione (stesso APK) esegue senza problemi su versioni di Android precedenti alla 5.

Ci sono un paio di discussioni a riguardo, sui social-network: 

Viene spiegato anche un workaround (che io ho applicato alla versione beta di ColorMapp stamattina) che consiste nel caricare le risorse grafiche (TBitmap) a runtime invece che lasciarle embedded nelle risorse dell'applicazione (evitando così che vengano caricate attraverso una chiamata a TBitmap.LoadFromStream, che sembra essere il punto critico del malfunzionamento).

Passi per applicare il workaround:

1) Salvare su disco tutte le bitmap di ogni form

(comodo se avete già caricato le immagini a design time, meno utile se state scrivendo una applicazione da zero)

Per semplificare il processo, ho scritto un pezzetto di codice che itera sui componenti di una form e salva tutte le bitmap delle TImage che trova.
Potete guardare la funzione SaveAllImagesToDisk della unit ImageDumpUnit.pas, che trovate in un mio repository GitHub:


Per esempio, nell'evento OnCreate della form ho aggiunto:

  {$IFDEF MSWINDOWS}
  SaveAllImagesToDisk(Self);
  {$ELSE}
  LoadAllImagesFromDisk(Self);
 {$ENDIF}

Eseguendo l'applicazione sulla platform Win32, vi verrà generata una cartella images_dump con delle sottocartelle (una per ogni form) con le immagini salvate in files PNG.
Il ramo $ELSE della direttiva di compilazione viene eseguito per esempio su Android, così l'applicazione Win32 fa il dump e l'applicazione Android cerca di caricare dinamicamente le immagini dallo storage del dispositivo.

2) Rimuovere tutte le bitmap dalle risorse (.fmx)

Il modo più semplice (ma richiede di agire manualmente su ogni singolo componente delle vostre form con una bitmap) consiste nel:
  • svuotare tutte le voci delle MultiResBitmap (eliminando tutte le versioni delle bitmap);
  • svuotare le TBitmap (es. le proprietà StartValue / StopValue delle TBitmapAnimation), aprendo il component editor e premendo il tasto Clear;
Se avete molte TBitmap e svuotarle manualmente vi sembra improponibile, potreste agire direttamente sul DFM, cercando tutte le occorrenze di Bitmap.

3) Caricare a run-time le immagini corrispondenti

Anche in questo caso potete sfruttare la funzione LoadAllImagesFromDisk della unit ImageDumpUnit.pas, avendo cura di chiamarla per esempio nell'evento OnCreate delle vostre form.

4) Aggiungere al deploy tutti i file PNG generati al passo 1

Attraverso il deployment manager di Delphi, aggiungete i file PNG in modo che vengano distribuiti con la vostra applicazione. Abbiate cura di indicare come RemotePath, il valore  'assets/internal/images_dump/' seguito dal nome della form corrispondente.


5) controllate di non avere altre TBitmap

Per esempio mi sono accorto che il problema si presenta anche con le TBitmap (non solo le TMultiResBitmap) presenti in molti componenti (es. le TBitmapAnimation). Per queste io ho risolto manualmente (mimando il comportamento di LoadAllImagesFromDisk ) ma potrebbe aver senso automatizzare il dump e il load dinamico anche di quelle.

Conclusione

E' tutto, seguendo questi passi io ho risolto il crash della mia applicazione XE7 ColorMapp su Android 5.0 Lollipop e attualmente ho una beta funzionante sul mio Nexus 5.
Conto di promuovere la versione beta in produzione appena fatti i test su qualche altro dispositivo.

Buon lavoro e a presto,
Andrea

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