© 2012 - CEFRIEL Costruire interfacce grafiche complesse Docente: Gabriele Lombardi

Slides:



Advertisements
Presentazioni simili
Windows Per questa parte: Elementi dell'interfaccia grafica.
Advertisements

1 Le s-espressioni. 2 Un nuovo esempio completo: le s-espressioni Sexpr 4 alberi binari (possibilmente vuoti) che hanno sulle foglie atomi (stringhe)
1 Progettazione gerarchica delle s- espressioni, utilizzando lereditarietà
Interfacce Java.
Microsoft SQL Server 2008 Utilizzo. Creazione DataBase CREATE DATABASE CREATE DATABASE Cinema.
WSDL (Web Services Description Language) Laurea Magistrale in Informatica Reti 2 (2006/07) dott. Federico Paoloni
JPA Overview della tecnologia. Java Persistence Architecture Definita nella JSR-220 EJB 3.0 ( Tentativo di unificare EJB2.1 con.
1 Processi e Thread Processi Thread Meccanismi di comunicazione fra processi (IPC) Problemi classici di IPC Scheduling Processi e thread in Unix Processi.
Alcune Classi Standard Object, Vettori. Esercizio dellultima volta Superclasse Persona Sottoclasse Libro.
Esercizio 2. Mostrare l'evoluzione dello stato (ambiente di classi, heap, pila, System.out) durante la valutazione delle seguenti dichiarazioni di classe.
Sequential Statements. – Il VHDL simula lo svolgersi in parallelo di varie operazioni – Loggetto fondamentale e il PROCESS – Un PROCESS contiene una serie.
E Windows SharePoint Services 2.0 Ivan Renesto Overview how to use Windows SharePoint Services.
Un DataBase Management System (DBMS) relazionale client/server.
Layouts and Graphics. component - container - layout Un Container contiene [0 o +] Components Il Layout specifica come i Components sono disposti nel.
Model – View - Controller
Esempio: Tombola! Parte seconda.
Unified Modeling Language class C {…} class B extends C {…} Esiste una notazione grafica per mostrare le relazioni di ereditarietà. Object StringC B Tutte.
J0 1 Marco Ronchetti Java Threads & Sincronizzazione.
Richiami di Java Multithreading. Threads (subclassing) public class A { public void a_method { C t = new C(); //C t = new C(String name); t.start(); …
Sezione: Costruttori Costruttori. Definizione dei costruttori Se per una classe A non scrivo nessun costruttore, il sistema automaticamente crea il costruttore.
prompt> java SumAverage
1 struct Pila { private: int size; int defaultGrowthSize; int marker; int * contenuto; void cresci(int increment); public: Pila(int initialSize) ; Pila();
Costruzione di Interfacce Lezione 10 Dal Java al C++ parte 1
1. Conoscere luso delle collezioni in Java Comprendere le principali caratteristiche nelle varie classi di Collection disponibili Saper individuare quali.
1 Esercitazione sui segnali Problema: creare un programma analizzatore di file testuali che prenda come argomenti il nome di un file e una sequenza di.
Modello dati LISTA LISTA: LISTA: sequenza finita di 0 o più elementi LISTA di tipo T: lista in cui tutti gli elementi sono dello stesso tipo T. es. lista.
Primi esempi di interfacce grafiche con Android
© CEFRIEL Ricettario dei principali pattern GoF Docente: Gabriele Lombardi
© CEFRIEL Cenni su XML in Java Docente: Gabriele Lombardi
2000 Prentice Hall, Inc. All rights reserved. 1 Capitolo 6: Classi e astrazione dati 1.Introduzione 2.Definizione delle strutture 3.Accedere ai membri.
1 laboratorio di calcolo II AA 2003/04 ottava settimana a cura di Domizia Orestano Dipartimento di Fisica Stanza tel. ( )
FONDAMENTI DI INFORMATICA III WfMC-1. FONDAMENTI DI INFORMATICA III WfMC-2 WFMC Cose WfMC Workflow Management Coalition (WfMC), Brussels, è unorganizzazione.
Gestione File System e I/O in Windows 2000 Implementazione del File System FAT-12, FAT-16, FAT-32 NTFS API relative al File System Gestione dei dispositivi.
New Features + Improvements Miglioramenti alle Situazioni contabili Distribuzione costi Intragruppo in registrazione fatture di acqusti Varie.
Sequence. CREARE UNA SEQUENCE CREATE SEQUENCE nome [INCREMENT BY n] [START WITH n] [MAXVALUE n | NOMAXVALUE] [MINVALUE n | NOMINVALUE] [CYCLE | NOCYCLE]
Componenti dell’architettura Oracle
1 Programmazione grafica 1 Daniele Marini. 2 Linguaggio di riferimento OpenGL: libreria di procedure che realizza un API (application programmers interface)
1Palma AM - G5 TUTORIAL GIMP per realizzare immagini con trasparenze e creare immagini con animazioni EXIT.
By Inter-Ware Soft. Tech. Introduzione all'uso del PC Massimo Sgambato.
Microsoft Access Maschere.
Il sistema operativo Sistema operativo (in breve) –È costituito dai programmi di gestione delle operazioni più elementari del computer –… gestione di vari.
ANDROID PROGRAMMING Questo approfondimento tematico è pensato per chi vuol imparare a programmare e creare software per gli smartphone con sistema operativo.
Muoversi tra le finestre
JavaScript Lezione 5 Tipizzazione ed operazioni tra tipi diversi Istruzioni di input.
Lambiente operativo. 2 Per avviare e poter utilizzare il computer è necessario un particolare programma che si chiama sistema operativo. Windows è un.
Corso di Elementi di Informatica
PRIMI DISEGNI CON CABRI Realizzato da Daniel Bulgarini e Matteo Co CLASSE 2°C LICEO PASCAL MANERBIO.
La risoluzione determina il dettaglio dell'immagine, quindi le massime dimensioni di stampa che potete raggiungere mantenendo una buona qualità. La fotocamera.
Primi passi con Windows: Gestione del Desktop Barra Applicazioni Menu Avvio ISTITUTO COMPRENSIVO N.7 - VIA VIVALDI - IMOLA Via Vivaldi, Imola.
C OME CREARE I FRAME. Dal pannello File fai doppio clic sul file default.html per aprire la pagina (figura 1.1). Figura 1.1 Il file default.html.
Personalizzazioni. Perché personalizzare Radio-Play Personalizzare Radio-Play permette al tuo brand di crescere e di diffondersi in modo gratuito e potenzialmente.
Il Booking Engine Html di HermesHotels è studiato per permettere I-Frame sui siti degli Hotels. Disponibile anche in modalità Pop- Up https, simile.
Sviluppo di una Xlet per la TV digitale terrestre Vademecum della regione marche 2006 Ventura Luca.
Tutorial relativo al Mio EBSCOhost. Benvenuti al tutorial dedicato a Mio EBSCOhost, verranno fornite le istruzioni per la configurazione e lutilizzo ottimizzato.
Modulo 1 bis Menù Incolla Esercitazione Un computer è quasi umano, a parte il fatto che non attribuisce i propri errori a un altro computer. (Anonimo)
Moduli o Form I Moduli permettono all'utente di immettere informazioni...
Fondamenti di Informatica Corsi di Laurea in Ingegneria Gestionale Canale AL ESERCITAZIONE 6 ARGOMENTI: LAVORARE CON CLASSI METODI DELLISTANZA, COSTRUTTORI.
INTERNET Internet è una rete a livello mondiale che permette alle persone di comunicare ed ad accedere a banca dati da qualunque parte del mondo e su qualunque.
Scoprirete che su Office non si può solo contare ma anche sviluppare.
Ese 2 (del 31 Marzo 2004). Mostrare l'evoluzione dello stato (ambiente di classi, heap, pila, System.out) durante la valutazione delle seguenti dichiarazioni.
Multiset. Progettare (specifica con identificazione delle eventuali astrazioni necessarie, incluse eccezioni, e implementazione) del tipo di dato Multiset,
VB.NET Sviluppo Applicazioni Desktop
Test con JUnit. zJUnit è un ambiente di test per programmi Java ySviluppato da Kent Beck É possibile usare JUnit allinterno di Eclipse per eseguire i.
Project Review Novembrer 17th, Project Review Agenda: Project goals User stories – use cases – scenarios Project plan summary Status as of November.
Project Review Novembrer 17th, Project Review Agenda: Project goals User stories – use cases – scenarios Project plan summary Status as of November.
1 Simulated multiple inheritance Sandro Pedrazzini Approfondimento Simulated multiple inheritance in Java.
SUBQUERY Chi ha un salario maggiore di quello di Abel? Occorre scomporre la query in due sotto problemi: MAIN : quali impiegati hanno un salario maggiore.
Collection & Generics in Java
SQL Developer Lanciare sqldeveloper (alias sul desktop) / c:\Oracle\sqldeveloper Associare tutti i tipi di file, se volete Tasto destro sulla spina “connection”
JDBC Java DataBase Connectivity SISTEMI ITIS B. CASTELLI Anno Scolastico
Transcript della presentazione:

© CEFRIEL Costruire interfacce grafiche complesse Docente: Gabriele Lombardi

© CEFRIEL The present original document was produced by CEFRIEL and the Teacher for the benefit and internal use of this course, and nobody else may claim any right or paternity on it. No right to use the document for any purpose other than the Intended purpose and no right to distribute, disclose, release, furnish or disseminate it or a part of it in any way or form to anyone without the prior express written consent of CEFRIEL and the Teacher. © copyright Cefriel and the Teacher-Milan-Italy-23/06/2008. All rights reserved in accordance with rule of law and international agreements.

© CEFRIEL Sommario SLIDECONTENUTO Progettare e prototipare Identificare Activity, menu, … Prototipare la GUI Menu e prefs in XML Come mostrare e organizzare menu e prefs AsyncTask Gestire task in background Disegnare Come lavorare su un canvas direttamente Touch Me Usiamo il touch screen per esplorare Persistenza su file Salvare contenuti multimediali e non

Progettiamo un esploratore di frattali Struttura dellinterfaccia dellapp: –prevediamo di permettere allutente la definizione di: opzioni di generazione del frattale: –tipo di frattale (nella v1.0 Julia o Mandelbrot, ma estendibile); –eventuali parametri del frattale (Julia ne ha 2, un punto su C). preferenze: –profondità di colore (e quindi di calcolo); –tavolozza dei colori; … –tutto questo in 2 Activity dedicate; –una Activity permetterà di interagire con il frattale; Per ogni frattale ci piacerebbe offrire: –una descrizione di come viene calcolato; –una descrizione delle sue origini storiche. © CEFRIEL FractalSetupActivity PreferencesActivityFractalExplorerActivity FractalComputationTask

GUI per lattività principale (setup del frattale) Attenzione alla generalizzazione: –ogni frattale ha parametri suoi; –posizione e zoom sono parametri che hanno tutti i frattali mostrati; –la lista dei parametri è "dinamica"; –vogliamo poter salvare e caricare dei setup di frattali precedenti; –ogni frattale è diverso… ma uguale! Disegniamo il modello dei dati: –classe Fractal: –serializzabile: per poterla salvare; –astratta per crearne tanti diversi; –classe Parameter: –con info sul parametro (estremi, intero); –per trattare i parametri uniformemente. © CEFRIEL Tipo Parametro … InfoEsplora SalvaCarica

RainbowPalette Struttura del modello di calcolo © CEFRIEL Fractal compute(PointF,Settings) getInfoKey(): int draw(bmp: Bitmap, level: int, …) > Callback rowCompleted levelCompleted Parameter getName(): String getMin(): float getMax(): float isInteger(): boolean Settings cx cy zoom depth palette MandelbrotJuliaPalette forIndex(index: int, depth: int) RedPaletteGreenPaletteBluePalette

Tipo di frattale e parametri Scelta del tipo di frattale: –scelta multipla equivalente di una ComboBox: Spinner utilizza una lista adapter; –allevento di selezione del frattale: creiamo unistanza della classe giusta; eliminiamo i controlli esistenti; creiamo i controlli nuovi per i parametri; Controlli dei parametri: –supportiamo solo parametri float o int; –supportiamo estremi min e max: definendo il tipo di input come decimale; –possiamo usare un EditText oppure una SeekBar: supporta solo interi tra 0 e N; lavoriamo con una percentuale… poi scaliamo e trasliamo noi i sistema di riferimento. © CEFRIEL

Tipo di frattale e parametri In onCreate preparo lo spinner con un adapter. Se un frattale viene scelto: –creo listanza della classe Fractal giusta; –aggiorno le componenti per i parametri. © CEFRIEL vFractalName = (Spinner)findViewById(R.id.main_fractalName); // Imposto ladattatore per la lista: ArrayAdapter adapter = new ArrayAdapter (this, android.R.layout.simple_spinner_item, getFractalNames()); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); vFractalName.setAdapter(adapter); vFractalName.setSelection(0); vFractalName.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() // Se scelto creo il frattale e aggiorno i parametri: public void onItemSelected(AdapterView adapterView, View view, int i, long l) { try { fractal = fractalClasses[i].getConstructor().newInstance(); updateParameters(); } catch (Exception e) { Log.e(TAG, "Cannot create the fractal", e); } public void onNothingSelected(AdapterView adapterView) { vFractalName.setSelection(0); // Seleziono il mandelbrot: } });

Tipo di frattale e parametri © CEFRIEL private void updateParameters() { vParameters.removeAllViews(); // Svuoto il layout: // Aggiungo i parametri: final Parameter[] parameters = fractal.getParameters(); for (int i=0; i<parameters.length; i++) { final int j = i; // Il parametro: final Parameter p = parameters[j]; TextView label = new TextView(this); // La label: label.setText(p.getName()); vParameters.addView(label); SeekBar bar = new SeekBar(this); // La SeekBar: bar.setMax(100); bar.setProgress((int)((fractal.getParameterValue(j) - p.getMin()) / (p.getMax() - p.getMin()) * 100)); bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() public void onProgressChanged(SeekBar seekBar, int ind, boolean b) { fractal.setParameterValue(j, // Cambio il parametro: (ind / 100.0f) * (p.getMax() - p.getMin()) + p.getMin()); public void onStartTrackingTouch(SeekBar seekBar) { public void onStopTrackingTouch(SeekBar seekBar) { } }); vParameters.addView(bar); } }

Tipo di frattale e parametri E se avessimo voluto un campo di testo? –Da configurare come solo numerico: supporta numeri con segno e con virgola. –Il resto è uguale, compresa letichetta. © CEFRIEL EditText editor = new EditText(this); // Uso un campo di testo: editor.setInputType(InputType.TYPE_CLASS_NUMBER | // Lo configuro come decimal: (p.isInteger()? 0: InputType.TYPE_NUMBER_FLAG_DECIMAL) | InputType.TYPE_NUMBER_FLAG_SIGNED); editor.setText(Float.toString(fractal.getParameterValue(j))); editor.setOnKeyListener(new View.OnKeyListener() { // Ne ascolto i public boolean onKey(View view, int i, KeyEvent keyEvent) { try { fractal.setParameterValue(j, Float.parseFloat(((EditText)view).getText().toString())); } catch (Throwable t) { } return true; } }); vParameters.addView(editor);

Un menu in XML Definiamo un menu usando lXML (inflater). © public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main_menu, menu); return super.onCreateOptionsMenu(menu); public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.main_menu_infos: new AlertDialog.Builder(this).setTitle(R.string.fractalInfos).setMessage(getResources().getString(fractal.getInfoKey())).setPositiveButton(R.string.btn_close, null).show(); return true; case R.id.main_menu_preferences: startActivity(new Intent(this, PreferencesActivity.class)); return true; case R.id.main_menu_explore: Intent i = new Intent(this, FractalExplorerActivity.class); i.putExtra(FRACTAL_EXTRA, fractal); i.putExtra(SETTING_EXTRA, settings); startActivity(i); return true; } return super.onOptionsItemSelected(item); }

Un menu in XML © CEFRIEL <item <item <item <!--item <item Tutte le stringhe per lutente sono esterne. Supporteremo nella prossima versione

Utilizzare le preferences per i settings Vogliamo salvare i Settings nelle preferences: –strumento: SharedPreferences; per ottenerlo abbiamo più possibilità: –context.getPreferences() strettamente legate allActivity –context.getSharedPreferences() condivise nellintera app –PreferenceManager.getDefaultSharedPreferences(context) preferenze dellapp gestite dal PreferenceManager usiamo le default per poter offrire lactivity: –subclassando PreferenceActivity possiamo descrivere le preferences in XML e lasciar gestre linterfaccia per impostarle. –ci organizziamo con read e write in Settings: attenzione, in futuro ci preoccuperemo dei tempi di accesso allo storage, ora no. Problemi: –ogni variazione delle preferenze deve essere salvata; –ogni variazione esterna deve aggiornare le preferenze. © CEFRIEL

Utilizzare le preferences per i settings © CEFRIEL <PreferenceScreen xmlns:android=" <PreferenceCategory <ListPreference android:key="zoom" android:defaultValue="0.5"/> <EditTextPreference android:key="cx" android:inputType="number|numberDecimal|numberSigned" android:defaultValue="-0.5"/> … <PreferenceCategory … Le preference sono organizzate in categorie Vari tipi possono essere inseriti (classi figlie di Preference) Inserite valori di default per il primo setting

Utilizzare le preferences per i settings © CEFRIEL // Aggiornamento dei settings da preferenze: prefs = PreferenceManager.getDefaultSharedPreferences(this); settings.read(prefs); // A fronte del cambiamento delle preferenze cambio i setting: prefs.registerOnSharedPreferenceChangeListener(onPreferencesChange); onCreate private SharedPreferences.OnSharedPreferenceChangeListener onPreferencesChange = new SharedPreferences.OnSharedPreferenceChangeListener() public void onSharedPreferenceChanged( // Le leggo: SharedPreferences sharedPreferences, String s) { settings.read(prefs); } protected void onDestroy() { // Elimino l'ascoltatore: prefs.unregisterOnSharedPreferenceChangeListener(onPreferencesChange); super.onDestroy(); } public void read(SharedPreferences prefs) { // Leggo dalle preferenze e scrivo i valori nei settings: setDepth(Integer.parseInt(prefs.getString("depth", "20"))); setCx(Float.parseFloat(prefs.getString("cx", "-0.5"))); setCy(Float.parseFloat(prefs.getString("cy", "0.0"))); setZoom(Float.parseFloat(prefs.getString("zoom", "0.5"))); setPalette(prefs.getString("palette", "Rainbow")); } Settings

Lesploratore di frattale usa un task asincrono Dobbiamo permettere allutente di fruire la UI: –durante i calcoli linterfaccia deve essere responsiva; –non possiamo eseguire i conti sul thread principale; –dobbiamo consentire linterrompibilità rapida; –dobbiamo mostrare un avanzamento lavori frequente; Soluzione da scegliere in generale: –un thread esterno con comunicazione asincrona; –la classe AsyncTask ha proprio questo scopo: estenderla implementando doInBackground eventualmente ovverride di "on*"; verificare frequentemente isCanceled: –nel qual caso terminare lesecuzione, il task termina qui. usiamo Void per i generics, vedremo + avanti; –avviamo il task con il metodo "execute"; –appena richiesto eseguiamo "task.cancel". © CEFRIEL

Il task © CEFRIEL private class FractalComputationTask extends AsyncTask protected Void doInBackground(Void... voids) { int N = 5; // Itero sui livelli: for (int i=N; i>=0; i--) { if (isCancelled()) return null; // Controllo per luscita forzata: // Eseguo il calcolo per un livello: fractal.draw(bitmap, i, i==N, settings, new Fractal.Callback() public boolean rowCompleted(Bitmap bitmap) { return isCancelled(); } // Uscita public void levelCompleted(int level, boolean isFirst) { // Blocco il canvas e ci disegno: canvas = holder.lockCanvas(); canvas.drawBitmap(bitmap, 0.0f, 0.0f, new Paint()); holder.unlockCanvasAndPost(canvas); } }); } return null; }

Disegnare su una surface Per poter disegnare dobbiamo: –avere una surface come view di visualizzazione; –ottenere un holder, «mantenitore» della bitmap; –creare una bitmap su cui lavorare off-line; –ascoltare gli eventi dellholder per gestire creazione della bitmap e distruzione del task. Quando dobbiamo veramente disegnare: –dobbiamo lockare il canvas su cui disegnare; –disegnarci su la nostra bitmap aggiornata; –«committare» la modifica allholder. © CEFRIEL

Inizializzazione (onCreate) © CEFRIEL // Inizializzo superficie e holder: surface = (SurfaceView)findViewById(R.id.explorerSurface); holder = surface.getHolder(); task = new FractalComputationTask(); // Il task: // Ascolto gli eventi dell'holder: holder.addCallback(new SurfaceHolder.Callback() public void surfaceCreated(SurfaceHolder surfaceHolder) { bitmap = Bitmap.createBitmap( // Creo la bitmap: surface.getWidth(),surface.getHeight(), Bitmap.Config.ARGB_8888); task.execute(); // Avvio un task asincrono per il disegno del frattale: public void surfaceChanged( SurfaceHolder surfaceHolder, int i, int i1, int i2) { public void surfaceDestroyed(SurfaceHolder surfaceHolder) { task.cancel(true); // Fermo il task: // Rilascio le risorse: bitmap = null; holder.removeCallback(this); } });

Un occhio di insieme (versione 1.0) © CEFRIEL FractalExplorerActivity bitmap surfaceholdercanvas FractalComputationTask fractal settings La surface ci da lholder che quando serve fornisce il canvas; lactivity gestisce il task che lavora su un altro thread; il task tramite fractal e settings scrive sulla bitmap di dobule- buffering creata da noi; per gli aggiornamenti viene creato un canvas su cui disegnare la bitmap; nel canvas unaltra bitmap è nascosta per mantenere limmagine mostrata allutente.

Refactoring: estraiamo una custom-view Troppe responsabilità nella stessa activity: –al fine di non rendere il codice troppo complesso: in generale conviene fattorizzarlo; funzionalità coese possono essere estratte; se ha senso vanno create view custom; –ogni view può gestire disegno e interazione con essa; –lactivity può comporre le view e gestire i menu. Creare una view custom è molto semplice: –creare una sottoclasse della view di interesse: nel nostro caso estendiamo SurfaceView; –override-are i metodi di interesse della view: –onDraw per disegnarne il contenuto (non draw); –onTouchEvent per gestire gli eventi touch; –spostiamo anche FractalComputationTask. © CEFRIEL

TouchMe Esperienza di interfaccia uomo-macchina: –Siggraph 2006, il multi-touch era già realtà: –Cosa si può fare con un dito? Selezione, trascinamento, tracciamento, tap, tap lungo, tap multipli… ~un mouse: –interfaccia differente, stessi gradi di libertà… –…più controllo di dimensione e pressione… »…ovvero? Parliamo di come sono fatti i sensori! Meno preciso ma più user-frendly. –Cosa si può fare con due dita? Quanto sopra «in parallelo» separatamente; descrivere trasformazioni di similarità: –traslazione, scala e rotazione tutto assieme. –Cosa si può fare con tre dita? trasformazioni 3d (ad es. si consideri la rotazione); più dita possono essere gestite ma sono complicate per lutente da coordinare. Casi di utilizzo frequente? –Scorrimento liste, selezione, cambio pagina, … –PTZ (Pan, Tilt, Zoom), ad es. di immagini nella gallery. Noi possiamo fare PTZ sul nostro frattale (con qualche calcolo). © CEFRIEL

Rendiamo il frattale esplorabile con le dita Eventi touch notificati con onTouchEvent: –ogni evento ha una posizione, e un codice; –metadati sono associati ad ogni evento: pressione, dimensione, … –da Android 2.0 si ha il supporto al multitouch: ogni evento descrive più pointers. Per gestire il touch interpretiamo gli eventi: –le sequenze hanno una struttura pressappoco fissa; –aggregazione di eventi possono generare nuove tipologie di eventi più complessi (es. pinch). © CEFRIEL ACTION_DOWN ACTION_MOVE ACTION_UP ACTION_MOVE … Traslazione

Gestire il pan (traslazione) Stato di inizio delloperazione di pan: –memorizzo la posizione di inizio (startX, startY); –la trasformazione dellimmagine è lidentità. Durante loperazione di pan: –aggiorno la trasformazione rispetto alla traslazione: (e.get()-startX, e.getY()-startY). –posto una richiesta di invalidazione della view… …ovvero dichiaro che andrà ridisegnata; –il metodo onDraw disegnerà sfondo e immagine. Al termine delloperazione di pan: –calcolo le nuove coordinate del frattale (cambiando nei settings in maniera permanente i valori cx e cy); –rendo identità la trasformazione; –avvio un nuovo calcolo per il frattale; –invalido la vista (verrà ridisegnata nel main thread). © CEFRIEL

Alcuni dettagli tecnici Invalidate e postInvalidate: –la prima va chiamata dal main (UI) thread; –la seconda accoda una richiesta (qualunque thread); onDraw: –chiamata da draw, da non override-are questultima in caso di SurfaceView (perderemmo le chiamate agli eventi SurfaceHolder.Callback); –se vogliamo che venga chiamato draw… …impostare setWillNotDraw(false); android.graphics.Matrix: –rappresenta una trasformazione affine in R 2 ; –traslazione, rotazione, scala, skew; –6 DoF, 6 coefficienti, geometria proiettiva; –canvas.setMatrix qualunque operazione di disegno può essere attuata «attraverso» una trasformazione affine. © CEFRIEL

Dal touch al multi-touch Ogni tocco in Android è potenzialmente multiplo: –ogni MotionEvent può contenere dati relativi a più tocchi contemporaneamente (pointers); –ogni pointer ha un id univoco durante una operazione; accesso ai dati di tocco: –numero puntatori indicato da getPointerCount; –eventi ACTION_DOWN/ACTION_POINTER_DOWN; –eventi ACTION_MOVE; –eventi ACTION_UP/ACTION_POINTER_UP; –…–… Troppi eventi… accorpiamoli assieme! –Se troppi eventi vengono generati di seguito vengono accorpati un una history accessibile dallevento: getHistorySize numero di eventi accorpati; getHistoricalX/Y coordinate del tocco. © CEFRIEL

Gesture detection fai-da-te Una possibilità per la gesture detection: –analizzare la sequenza di eventi di tocco; –mantenere informazioni salienti sulla sequenza; –identificare le azioni desiderate. Semplice ma complesso: –in certe situazioni (come la nostra) risulta semplice; –in altre risulta estremamente complesso «a mano»: immaginiamo un tocco «circolare». Per varie tipologie di gesture esistono diverse classi di supporto che analizzano la sequenza: –dobbiamo fornirgli gli eventi che riceviamo; –dobbiamo registrare un nostro ascoltatore di gesture. © CEFRIEL

Il GestureDetector Cosa fa un gestureDetector? –Riceve (da noi) una sequenza di MotionEvent; –la analizza e produce eventi «più complessi»: li ascoltiamo con unascoltatore: –GestureDetector.OnGestureListener eventi semplici: »onDown, onFling, onLongPress, onScroll, onShowPress, onSingleTapUp –GestureDetector.OnDoubleTapListener eventi di tap: »onDoubleTap, onDoubleTapEvent, onSingleTapConfirmed Come utilizzarlo? –Creiamo il detector e registriamo i listener; –nella nostra view custom catturiamo gli eventi di touch facendo ovverdide di onTouchEvent; –li notifichiamo al gesture detector con onTouchEvent; –agiamo nei listener in presenza degli eventi. © CEFRIEL

Gesture complesse: le gesture library In alcune app con particolari caratteristiche: –ad esempio dove linterazione utente è importante: videogiochi, ebook-reader, … –eventualmente per un range limitato di azioni es.: solo volta pagina in un lettore di libri; diventa utile interpretare le sequenze di tocchi: –una gesture è formata da una o più sequenze (stroke) che formano un pattern noto a priori; –ogni sequenza contiene uno o più punti nel percorso; una libreria di gesture è una struttura dati che contiene una o più descrizioni di gesture: –Android ci fornisce gli strumenti per identificarle automaticamente tramite una view di overlay. © CEFRIEL

Creiamo e usiamo una gesture library Creiamo un gestore di note brevi. Usiamo direttamente lemulatore: –app Gesture Builder creata apposta; –eliminiamo eventuali gesture; –creiamo le nostre con nome; –copiamo /mnt/sdcard/gestures nel progetto in res/raw/gestures. Prepariamo il layout: © CEFRIEL <android.gesture.GestureOverlayView xmlns:android=" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:eventsInterceptionEnabled="true"> <ListView android:layout_width="fill_parent" android:layout_height="fill_parent"/>

Detection delle gesture © CEFRIEL // Carico la libreria di gesture: final GestureLibrary gestures = GestureLibraries.fromRawResource(this, R.raw.gestures); if (!gestures.load()) { Toast.makeText(this, "Impossibile caricare le gesture", Toast.LENGTH_LONG).show(); finish(); } gesturesOverlay.addOnGesturePerformedListener( new GestureOverlayView.OnGesturePerformedListener() public void onGesturePerformed( GestureOverlayView gestureOverlayView, Gesture gesture) { // Verifico se una gestura di interesse è arrivata: ArrayList predictions = gestures.recognize(gesture); if (predictions.size() > 0 && predictions.get(0).score > 1.0) { // Identifico l'accaduto (in questo caso aggiungo alla lista): String action = predictions.get(0).name; if ("action_write".equals(action)) doWrite(); else if ("action_delete".equals(action)) doDelete(); else if ("action_save".equals(action)) doSave(); else if ("action_load".equals(action)) doLoad(); } } });

Menu contestuali Menu menu menu… –…per richiedere azioni alla nostra app… –…se lazione dovesse riguardare un item in una lista? Menu di contesto: –oltre a richiedere unazione trasporta il contesto in cui la richiesta è avvenuta (in altre GUI… tasto destro); –il menu si presenta come una tendina di selezione; –possiamo appendere menu di contesto alle view; –sono menu di contesto… li descriviamo in XML. © public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); getMenuInflater().inflate(R.menu.context_menu, menu); public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); switch (item.getItemId()) { case R.id.menu_delete: doDeleteItem(info.position); return true; case R.id.menu_edit: doEdit(info.position); return true; } return super.onContextItemSelected(item); } // Ci registriamo per un menu contestuale: registerForContextMenu(getListView());

Tornando a fractal explorer… Salviamo una immagine nella sd-card: –aggiungiamo un menu in FractalExplorerActivity; –per la voce «Save as image»: chiediamo un nome di file; salviamo nella cartella delle immagini; –per i permessi abbiamo bisogno di: –android.permission.WRITE_EXTERNAL_STORAGE © CEFRIEL FileOutputStream out = null; try { File extDir = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES); extDir.mkdirs(); out = new FileOutputStream( new File(extDir, input.getText().toString() + ".png")); surface.getBitmap().compress( Bitmap.CompressFormat.PNG, 40, out); out.getFD().sync(); } catch (Exception e) {…} finally { try { out.close(); } catch (Throwable t) { } }

rows++; … if (callback!=null) callback.progress((float)rows/totalRows); Aggiungiamo una barra di progresso NON vogliamo aggiungerla a mano: –potremmo aggiungere una SeekBar a mano: dovremmo per forza inserirla nel layout; noi vogliamo inserirla nella barra del titolo; –non possiamo usare il layout full-screen: eliminare nel manifest lattributo: –dobbiamo interagire con lUI creata da Android: –requestWindowFeature(Window.FEATURE_PROGRESS); dobbiamo fornirci le informazioni dal task asincrono: © CEFRIEL private int rows; … rows = 0; int totalRows = (int)((2-1.0/N)*bitmap.getHeight()) public interface ProgressCallback { public void start(); public void progress(float percentage); public void done(); }

Aggiungiamo una barra di progresso © CEFRIEL surface.setCallback(new FractalSurface.ProgressCallback() public void start() { runOnUiThread(new Runnable() public void run() { // Inizializzo la progress-bar: setProgress(0); setProgressBarVisibility(true); } }); public void progress(final float percentage) { runOnUiThread(new Runnable() public void run() { // Aggiorno lindicatore di progresso: setProgress((int) (percentage * 10000)); } }); public void done() { runOnUiThread(new Runnable() public void run() { // Nascondo la progress-bar: setProgressBarVisibility(false); } }); } });

Parametri: uno strumento per valorizzarli La SeekBar non ci piace: –non mostra il valore dellattuale selezione; –ci costringe a gestire «male» gli estremi; –mostra una grafica da progress-bar; –il puntatore non è facilissimo da «acchiappare». Soluzione? Ce la creiamo noi! –Creiamo un nuovo componente figlio di View; –lo realizziamo come composizione di altri componenti; –(potremmo disegnare da zero il componente.. scomodo) –lo personalizziamo come ci sembra meglio; –lo usiamo mille volte come building-block. © CEFRIEL

Progettiamo una view custom © CEFRIEL minmaxnome valore Barra di scorrimento Manopolina di selezione Zona di spostamento del valore Zona dei testi informativi Zona «calda» di attivazione selezione Valore attuale (segue la manopola) Nome parametro Valore massimoValore minimo

Una classe figlia di view Un costruttore nostro: –non standard, non permette lutilizzo dellXML; –riceve un parametro da controllare. © CEFRIEL public Slider(Context context, Parameter parameter) { super(context); this.parameter = parameter; barPaint = new Paint(); // Pennello per la barra di scorrimento. barPaint.setColor(Color.GRAY); barPaint.setAntiAlias(true); barPaint.setStrokeWidth(barWidth); barPaint.setStrokeCap(Paint.Cap.ROUND); sliderPaint = new Paint(); // Pennello per la manopola. sliderPaint.setColor(Color.LTGRAY); sliderPaint.setAntiAlias(true); sliderPaint.setStrokeWidth(sliderWidth); sliderPaint.setStrokeCap(Paint.Cap.ROUND); numbersPaint = new Paint(); // Pennello per le scritte. numbersPaint.setColor(Color.LTGRAY); numbersPaint.setAntiAlias(true); numbersPaint.setFakeBoldText(true); numbersPaint.setTextSize(textHeight); /* … */ }

Una classe figlia di view Ci disegniamo nello spazio concessoci: © CEFRIEL // Informazioni: Rect rect = new Rect(); getDrawingRect(rect); float pos = (value-parameter.min)/(parameter.max-parameter.min)* (rect.right-getPaddingRight()-rect.left); float fromv = (rect.top+2*textHeight); float midv = (fromv+rect.bottom)/2.0f; // Disegnamo i valori estremi: numbersPaint.setTextAlign(Paint.Align.LEFT); canvas.drawText(formatNumber(parameter.min), rect.left, rect.top + textHeight, numbersPaint); numbersPaint.setTextAlign(Paint.Align.RIGHT); canvas.drawText(formatNumber(parameter.max), rect.right, rect.top + textHeight, numbersPaint); numbersPaint.setTextAlign(Paint.Align.CENTER); canvas.drawText(formatNumber(value), pos, rect.top + 2 * textHeight, numbersPaint); canvas.drawText(parameter.name,(rect.left+rect.right)/2, rect.top+textHeight,numbersPaint); // Disegnamo lo slider con la posizione attuale: canvas.drawLine(rect.left + barWidth, midv, rect.right - barWidth, midv, barPaint); canvas.drawLine(pos,fromv+sliderWidth,pos,rect.bottom-sliderWidth,sliderPaint); canvas.drawLine(pos,fromv+sliderWidth+barWidth, pos,rect.bottom-sliderWidth-barWidth,barPaint);

Misurare si.. Ma non a caso! In Android per misurare i widget… –…gli si chiedono le dimensioni minime occupate: override di onMeasure; –viene poi delegato il layout per leffettiva decisione dello spazio disponibile e delle dimensioni finali: basiamoci quindi su quelle forniteci; nel nostro caso 2 righe di testo e laltezza dello slider impongono solo unaltezza minima: © protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(getMeasuredWidth(), 2*textHeight + 4*(barWidth+sliderWidth)); }

Lasciamoci toccare… non troppo! Ci sono almeno 2 modi per intercettare gli eventi di tocco dallinterno di una view custom: –la prima labbiamo già vista: onTouchEvent; –la seconda consiste nel registrare un listener: –permette di agire anche senza creare una classe figlia. © CEFRIEL setOnTouchListener(new OnTouchListener() public boolean onTouch(View view, MotionEvent event) { // Dove ero? Rect rect = new Rect(); getDrawingRect(rect); float oldPerc = (value-parameter.min)/(parameter.max-parameter.min); float sliderx = rect.left + oldPerc*(rect.right-rect.left); // Reagisco solo in prossimità dallo slider: float x = event.getX(), y = event.getY(); if (event.getAction()==MotionEvent.ACTION_DOWN && !(y-rect.top > 2*textHeight && Math.abs(x-sliderx) < 3*sliderWidth)) return false; // Catturiamo gli eventi di tocco per spostare lo slider: float perc = (event.getX()-rect.left)/(rect.right-rect.left); value = parameter.min + perc*(parameter.max-parameter.min); if (parameter.integer) value = Math.round(value); // Fatto, gestisco gli eventi: if (onValueChangeListener!=null) onValueChangeListener.valueChanged(value); invalidate(); return true; } });

Giochiamo col nuovo componente… …inserendolo al posto della SeekBar: © CEFRIEL Slider slider = new Slider(this, p); slider.setValue(fractal.getParameterValue(j)); slider.setOnValueChangeListener(new Slider.OnValueChangeListener() public void valueChanged(float value) { // Cambio il parametro: fractal.setParameterValue(j, value); } }); vParameters.addView(slider); Permettiamo lo scorrimento verticale: <ScrollView android:layout_height="wrap_content" android:layout_width="fill_parent"> <LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent" android:orientation="vertical"/> Come si propagano gli eventi???

Altro??? Il framework per la creazione di UI in Android… –…è ampio, semplice e divertente da usare; –molti componenti possono essere usati (giochiamoci); –alcune funzionalità nuove e interessanti le vedremo nel «capitolo» delle novità da Honeycomb in avanti; –molto più di quanto abbiamo visto può essere definito in XML… giochiamoci assieme! Non abbiamo ancora affrontato problemi come: –la persistenza dei dati su database; –linterazione tra app (es con GoogleMaps); –laccesso alla rete, ad esempio per servizi RESTful; –lutilizzo dei sensori (camera, accelerometro, …). © CEFRIEL