Java per Android -...

81
Tecnologie - Classe Quinta robertomana.it Java per Android pag 1 Java per Android Rev. 5.3 del 07/03/2020 Android SDK …………………………………………………………………..………………..……… 2 SDK Manager ……………..………………………………………..………………………..………… 4 AVD Manager ……………..………………………………………..………………………..………… 5 Activity e Layout ……………………………………………..……………………….…….………… 7 Il file AndroidManifest.xml .…….....……………………………………………...……....………… 7 Il file strings.xml ..……………….....……………………………………………………...………… 8 La programmazione, Widget e View, Gestione dell ID, il file R …..……………………….……… 8 La gestione degli eventi …..………………………………………………………………………… 10 Concetto di Context ..………………………………………………………….….……….………… 12 Le finestre di popup : i Toast …………………………………………………….………………….. 13 AlertDialog e InputDialog …………………………..………………………….…………………….. 14 Utilizzo dei principali controlli .…………….……………………………………………....………… 16 Il widget EditText ..………………………………………………………………………....………… 16 Check Box, Radio Button ……………………………………….………………….……………….. 17 Il widget ImageView ..………………………………………………………..…….……...………… 17 Creazione dinamica dei controlli ……………………………………………………….………….. 19 Approfondimenti sulla Gestione dell’ID …………………………………………….…………….. 19 List e ArrayList ……………………………………………………………..………….……………….. 20 La scalatura delle immagini : PX vs DP …………….………………..……….………………….. 22 Principali Proprietà Comuni ……………………..………………………………….……………….. 24 I Layout ……………………..………………………….…………………………….……………….. 25 Accesso ai controlli di un layout ………………………….……….………………………..…..….. 30 La gestione degli sfondi tramite la classe ColorDrawable ….……………….………………….. 31 Gestione dello schermo: dimensioni e rotazione …………………………….………………….. 32 Intent ……………………………………………………….………………………………………….. 33 Utilizzo degli Intent impliciti …………..………………….………………………………………….. 34 Richiamo di una activity mediante un intent esplicito …..………………………………………….. 36 Ciclo di vita di una Activity …………………………………………………………….……………… 37 Passaggio di parametri ad una Activity …………………………………………..……………….. 38 Menù ………………………………………………………………………………….………………. 41 Device File Explorer ………………………………………………………………………………….. 46 Accesso al File System …………………………………………………………..………………….. 47 ListView e Adapter ………………………………..…………………………………………………. 49 Spinner e GridView ………………………………..…………………………………………………. 52 Creazione di un Adapter personalizzato ……………………..……………………………………. 53 Shared Preferences ……………………….…………………………..………………….………….. 56 La gestione dei thread : Il timer …………………………………………………..………………. 56 Basic Activity ……………………..……………………………………………………………………. 58 Drawer Activity …………………..……………………………………………………………………. 60 La gestione dei permessi …………………………………….…….……………………………….. 62 Accesso ad un web service HTTP …………………………..…….……………………………….. 63 Gestione di uno stream Json ……………………..…….……………………………………..…….. 68 Richiesta di una immagine ………………….…………….……………………………...………….. 70 Gestione di uno stream XML ……………………….…………………………………...………….. 71 Accesso a SQLite ………………………………………….……………………………...………….. 75 Utilizzo sei Sensori …………..…….………………………………………..……………………….. 78 Services And Notification ………………………..…….…………………………………….……….. 80

Transcript of Java per Android -...

Page 1: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 1

Java per Android

Rev. 5.3 del 07/03/2020

Android SDK …………………………………………………………………..………………..……… 2 SDK Manager ……………..………………………………………..………………………..………… 4 AVD Manager ……………..………………………………………..………………………..………… 5 Activity e Layout ……………………………………………..……………………….…….………… 7 Il file AndroidManifest.xml .…….....……………………………………………...……....………… 7 Il file strings.xml ..……………….....……………………………………………………...………… 8 La programmazione, Widget e View, Gestione dell ID, il file R …..……………………….……… 8 La gestione degli eventi …..………………………………………………………………………… 10 Concetto di Context ..………………………………………………………….….……….………… 12 Le finestre di popup : i Toast …………………………………………………….………………….. 13 AlertDialog e InputDialog …………………………..………………………….…………………….. 14

Utilizzo dei principali controlli .…………….……………………………………………....………… 16 Il widget EditText ..………………………………………………………………………....………… 16 Check Box, Radio Button ……………………………………….………………….……………….. 17 Il widget ImageView ..………………………………………………………..…….……...………… 17 Creazione dinamica dei controlli ……………………………………………………….………….. 19 Approfondimenti sulla Gestione dell’ID …………………………………………….…………….. 19 List e ArrayList ……………………………………………………………..………….……………….. 20 La scalatura delle immagini : PX vs DP …………….………………..……….………………….. 22 Principali Proprietà Comuni ……………………..………………………………….……………….. 24 I Layout ……………………..………………………….…………………………….……………….. 25 Accesso ai controlli di un layout ………………………….……….………………………..…..….. 30 La gestione degli sfondi tramite la classe ColorDrawable ….……………….………………….. 31 Gestione dello schermo: dimensioni e rotazione …………………………….………………….. 32

Intent ……………………………………………………….………………………………………….. 33 Utilizzo degli Intent impliciti …………..………………….………………………………………….. 34 Richiamo di una activity mediante un intent esplicito …..………………………………………….. 36 Ciclo di vita di una Activity …………………………………………………………….……………… 37 Passaggio di parametri ad una Activity …………………………………………..……………….. 38

Menù ………………………………………………………………………………….………………. 41 Device File Explorer ………………………………………………………………………………….. 46 Accesso al File System …………………………………………………………..………………….. 47 ListView e Adapter ………………………………..…………………………………………………. 49 Spinner e GridView ………………………………..…………………………………………………. 52 Creazione di un Adapter personalizzato ……………………..……………………………………. 53 Shared Preferences ……………………….…………………………..………………….………….. 56 La gestione dei thread : Il timer …………………………………………………..………………. 56

Basic Activity ……………………..……………………………………………………………………. 58 Drawer Activity …………………..……………………………………………………………………. 60

La gestione dei permessi …………………………………….…….……………………………….. 62 Accesso ad un web service HTTP …………………………..…….……………………………….. 63 Gestione di uno stream Json ……………………..…….……………………………………..…….. 68 Richiesta di una immagine ………………….…………….……………………………...………….. 70 Gestione di uno stream XML ……………………….…………………………………...………….. 71 Accesso a SQLite ………………………………………….……………………………...………….. 75 Utilizzo sei Sensori …………..…….………………………………………..……………………….. 78 Services And Notification ………………………..…….…………………………………….……….. 80

Page 2: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 2

Android SDK Il SO Android usa un kernel Linux dalla versione 2.6 in avanti (dunque con supporto ai multithread). Sopra c‟è Android Run Time (ART) basato sulle Librerie Java Core che sono una versione ridotta delle librerie SE. Lo pseudo codice prodotto dai compilatori android viene tradotto a run time in un eseguibile vero e proprio mediante un compilatore Just In Time che legge lo pseudocodice e crea un eseguibile vero e proprio. Android è stato originariamente sviluppato da una piccola azienda, la Android Inc, ma nel 2005 Google ne acquistò i diritti e decise di rilasciare Android in modalità open source, il che significa che chiunque voglia utilizzare tale sistema può farlo semplicemente scaricando l‟intero codice sorgente. Tuttavia i produttori di hardware possono aggiungere le proprie estensioni al sistema e personalizzare Android per differenziare i propri prodotti dagli altri Le applicazioni per Android sono distribuite mediante files .APK (Android Package) che sono files compressi in un formato simile al JAR. I file .apk possono essere aperti e ispezionati utilizzando applicativi comuni come 7-Zip, Winzip Winrar o X-Plore (in smartphone Android o Symbian). La presentazione ufficiale della prima versione del "robottino verde" avvenne il 5 novembre 2007 da parte della neonata OHA (Open Handset Alliance), un consorzio di aziende del settore Hi Tech che include Google, produttori di smartphone come HTC e Samsung, operatori di telefonia mobile come Sprint Nextel e T-Mobile, e produttori di microprocessori come Qualcomm e Texas Instruments. Oggi copre il 78 % del mercato nella vendita di smartphone / tablet:

Uno dei fattori principali che determinano il successo (o meno) di una piattaforma per dispositivi mobili è la varietà di applicazioni che essa mette a disposizione degli utenti. A partire dalla fine del 2008, Google ha messo a disposizione degli utenti l‟Android Market (oggi Google Play), un‟applicazione on-line attraverso la quale gli utenti possono installare applicazioni aggiuntive sui propri dispositivi. Su Google Play sono disponibili sia applicazioni gratuite che a pagamento.

Page 3: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 3

Ogni aggiornamento o release, similmente a quanto accade per molte versioni di Linux, segue un ordine alfabetico e una precisa convenzione per i nomi, che in questo caso sono quelli di dolci:

versione 1.5 Cupcake (30 aprile 2009 – API Level 3) versione 1.6 Donut (30 aprile 2009 – API Level 4) versione 2.0 Eclair (26 ottobre 2009 – API Level 5) versione 2.2 Froyo (20 maggio 2010 – API Level 8) versione 2.3 Gingerbread (6 dicembre 2010 – API Level 9) versione 3.0 Honeycomb (22 febbraio 2011 – API Level 11) versione 4.0 Ice Cream Sandwich (19 ottobre 2011 – API Level 14) versione 4.1 Jelly Bean (9 luglio 2012 – API Level 16) versione 4.2 Jelly Bean (13 novembre 2012 – API Level 17) versione 4.3 Jelly Bean (24 luglio 2013 – API Level 18) versione 4.4 Kit Kat n seguito ad un accordo con la Nestlé (31 ottobre 2013 – API Level 19) versione 5.0 Lollipop lanciata il 3 novembre 2014 (API Level 21) versione 5.1 Lollipop lanciata il 9 marzo 2015 (API Level 22) versione 6.0 Marshmallow lanciata il 5 ottobre 2015 (API Level 23) versione 7.0 Nougat lanciata il 22 agosto 2016 (API Level 24) versione 7.1 Nougat lanciata il 20 ottobre 2016 (API Level 25) versione 8.0 Oreo lanciata il 21 agosto 2017 (API Level 26) versione 8.1 Oreo lanciata il 25 ottobre 2017 (API Level 27) versione 9.0 Pie lanciata il 6 agosto 2018 (API Level 28)

Ambiente di sviluppo

Il 12 novembre 2007 l'OHA ha rilasciato il software development kit SDK che include: gli strumenti di sviluppo, le librerie, un emulatore del dispositivo, la documentazione (in inglese), alcuni progetti di esempio, tutorial e altro. E‟ disponibile per:

qualsiasi piattaforma Linux,

qualsiasi piattaforma x86 compatibile che usi un sistema operativo Windows (da XP in avanti)

Mac OS X, dalla versione 10.4.8,

L'IDE ufficialmente supportato per lo sviluppo di applicazioni per Android è Eclipse, per il quale è fornito un SDK ufficiale denominato ADT (Android Development Tools), che contiene tutte le librerie necessarie allo sviluppo.

Versioni diverse dell‟SDK possono convivere sulla stessa macchina, Nelle ultime versioni sono disponibili API per Google Maps, Facebook, mixPanel (statistiche) Compilzione

Java Source Compilatore javac Class files dx legge i class files e le risorse e genera i .dex files (Dalvik EXecutable, dove Dalvik era la vecchia Android Virtual

Machine oggi sostituita da ART) jar (compattatore) legge i files dex e genera un file finale .apk che è il file tipico da installare su un sistema Android

Page 4: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 4

L‟APK viene inviato direttamente all‟emulatore o al dispositivo vero e proprio.

Un file APK (Android Package) è un archivio che contiene le seguenti cartelle:

RES (risorse: costanti, form, immagini)

META-INF contenente i certificati (a chiave pubblica) che garantiscono riguardo alla bontà della app che si sta scaricando.

ed i seguenti files:

AndroidManifest.xml ( file xml che descrive l‟applicazione)

class.dex che è lo pseudo eseguibile vero e proprio che verrà eseguito dall‟Android Run Time

A run time viene generato un processo diverso per ogni applicazione da eseguire.

Installazione dell’SDK

Occorre innanzitutto installare la Java JDK attualmente alla versione 1.8

Occorre quindi scaricare ed installare Android SDK scaricabile da : http://developer.android.com/sdk/index.html

Nelle ultime versioni di Android Studio l’intera SDK viene installata insieme ad Android Studio all‟interno di un percorso definito in fase di installazione. L‟SDK è costituita da 2 applicazioni principali:

Android SDK Manager (gestore dei pacchetti)

Android AVD Manager (gestore degli emulatori)

SDK Manager e AVD manager sono accessibile dall‟interno di Android Studio dal menù Tools.

Nelle installazioni più vecchie per lanciare i due pacchetti occorreva andare nella cartella TOOLS di Android SDK

ed eseguire il file android.bat. Successivamente all’interno della cartella principale erano stati aggiunti due

comandi batch uno per ciascuna delle 2 utility.

SDK Manager

SDK Manager è un gestore di pacchetti che consente di stabilire quali versioni di sviluppo installare / disinstallare sulla macchina.

Selezionando Show Package Details si vedono i dettagli di ciascuna platform. Per ogni versione che si intende utilizzare occorre installare :

SDK Platform Eventualmente:

Google APIs

SDK Build-tools

Le altre voci sono principalmente emulatori che devono essere installati SOLO SE si intende utilizzarli. AVD Manager – Creazione di un device

Per poter provare una applicazione, occorre creare un emulatore di dispositivo, cioè un AVD (Android Virtual Device). Menù Tools / AVD Manager / pulsantino in basso a sinistra Create Virtual Device

Come dispositivo di prova va benissimo Nexus 4 4,7” che non è troppo pesante come utilizzo di RAM oppure Pixel 2

Page 5: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 5

Riguardo alla RAM, CPU, etc, confermare i valori proposti, Selezionare quindi il System Image da installare (es API level 29). Per avviarlo selezionarlo e fare Start

La finestra che viene proposta al momento del lancio fa si che l‟emulatore venga aperto all‟interno di una finestra di dimensioni ridotte più facilmente trascinabile e riposizionabile sul video.

Un ottimo emulatore di terze parti è scaricabile da: www.genymotion.com/fun-zone Scaricabile gratuitamente previa registrazione.

Creazione di applicazioni tramite Android Studio

Una applicazione Android è identificata da un package univoco. packege è sinonimo di namespace. IntelliJ Idea definisce automaticamente il package come package="com.example.appName"

Nel nome del package ci deve essere almeno un puntino. Il primo esercizio

Start a new Android Studio Project

Imposatre il path di lavoro che verrà riproposto per tutti gli esercizi successivi. Assegnare un Application Name univoco. Se non si modifica il path nella parte inferiore, quando si assegna l‟Application Name Android Studio riporta lo stesso nome anche nel text box inferiore

Nella 2° finestra viene richiesto soltanto l‟SDK minimo. Quello proposto (18) va benissimo

Nella 3: finestra scegliere Empty Activity

Nella 4: finestra vengono proposti il nome dell‟Activity e quello del layout Struttura di una applicazione Android

Una applicazione Android è articolata nelle seguenti sottocartelle:

src - sorgenti java

res - risorse (layout, values, drawable, animazioni)

gen – autogenerati (R.java)

bin – compilati (APK e DEX)

Nel combo box della finestra di progetto è possibile scegliere fra diverse voci.

Android visualizza soltanto i files relatvi allo sviluppo

Project visualizza invece tutti i file relativi al progetto, compresa l‟apk finale

Page 6: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 6

Componenti di una applicazione Android

Una applicazione può utilizzare quattro tipi di componenti :

Activity - è il codice che gestisce un layout (form)

Service - sono eseguiti in background e non hanno interfaccia grafica Broadcast Receiver

Content Provider (fornitore di contenuti)

I servizi non sono in grado di inviare nulla a monitor I servizi al massimo possono scrivere sul notification Manager

Le activity NON possono accedere al notification Manager Una applicazione più avere più activity e più services Una app non può lanciare un‟altra app, ma può lanciare una activity di un‟altra app Activity

Un'activity rappesenta una classe che consente di interagire con gli elementi di un layout.

Una Empty Activity è una classe che estende (cioè eredita da) AppCompatActivity, della quale occorre ridefinire il metodo onCreate. Una Activity vuota presenta inizialmente il seguente codice:

public class MyActivity extends AppCompatActivity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

Il metodo onCreate viene richiamato al momento dell‟avvio dell‟Activity.

savedInstanceState è lo stato dell‟activity salvato su disco in un‟area di sistema. Lo stato viene

automaticamente iniettato a onCreate() al momento del lancio

Per prima cosa onCreate() deve richiamare il metodo onCreate() della superClasse

setContentView(R.layout.main) è un metodo dell‟Activity che consente di associare

all‟Activity un Layout contenuto nel file main.xml della cartella res/layout del progetto corrente. Una Activity può gestire più Layout. In ogni momento ci sarà un unico layout attivo

Layout

I Layout sono memorizzati nella cartella res / layout / main.xml (il nome del layout deve essere interamente scritto in minuscolo)

Il designer del layout è costituito da due finestre :

Design contiene il layout grafico della form completabile tramite trascinamento

Text contiene il codice xml relativo alla creazione dei controlli posizionati sulla form.

All‟interno del layout occorre posizonare un on oggetto Layout che per default è un ContraintLayout. Esistono anche altri tipi di layout (LinearLayout, TableLayout, FrameLayout). L’oggetto Layout è molto simile a quelli usati in Java all’interno dei Panels

All‟interno del Layout si possono posizionare i Widget (controlli) di interazione con l‟utente. Si può indifferentemente scrivere il codice xml oppure trascinare gli oggetto sul designer.

Page 7: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 7

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height=" match_parent" <TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="Hello World"

/>

</LinearLayout>

Il widget TextView è equivalente alla Label di C#.

Il valore match_parent significa ricoprire l‟intera area del genitore (cioè a tutto schermo)

Il valore wrap_content adatta le dimensioni del widget alle dimensioni del contenuto.

Il file AndroidManifest.xml

Il file Manifest.xml è un file in cui devono essere registrati tutti i componenti appartenenti al progetto. E‟ l‟equivalente del file di Progetto di C#. Le principali informazioni contenute sono:

L’elenco delle Activity utilizzate dall‟applicazione

I permessi necessari per il funzionamento dell‟applicazione

La versione minima di sdk su cui l‟applicazione può essere eseguita

il nome del package

icon = icona da utilizzare per rappresentare l‟applicazione sul dispositivo

label = Nome identificativo dell‟applicazione

theme = tema utilizzato dalla applicazione

eventuali intent impliciti che l‟applicazione è in grado di elaborare

package="com.example.roberto.app01_eventi"

<application

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme">

<activity android:name=".MainActivity">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

Per ogni Activity vengono memorizzati il name e le caratteristiche dell‟Activity :

- I valori MAIN e LAUNCHER indicano che l‟ Activity può essere lanciata direttamente da SO.

Tutte le Activity di questo tipo verranno visualizzate sul desktop del dispositivo.

- Se invece l‟activity presenta il valore category = DEFAULT significa che si tratta di una Activity

secondaria richiamabile soltanto da un‟altra Activity. In questo caso all‟interno di action è

possibile specificare un nome che potrà essere utilzzato per avviare l‟activity. Nel caso di Activity di tipo DEFAULT la sezione <intent-filter> può anche essere omessa.

Page 8: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 8

Il file res / values / strings.xml

Questo file consente di definire delle costanti di parametrizzazione dell‟applicazione, nel senso che l‟applicazione può usare queste stringhe in qualsiasi segmento di codice. Esempio di file strings.xml

<resources>

<string name="app_name">app01_eventi</string>

<string name="titolo">Home Page</string>

</resources>

La stringa app_name consente di definire il nome visualizzato sulla barra superiore della app

Utilizzo delle strings all’interno del layout

<TextView android:text = "@string/titolo" />

Utilizzo delle strings all’interno del codice dell’Activity

Per leggere da codice le stringhe memorizzate all‟interno del file strings.xml occorre utilizzare il metodo this.getString()

txt.setText("Hello " + this.getString(R.string.titolo));

gradle

gradle è un compattatore mirato al packaging dell‟apk finale, simile ad altri strumenti di build automation come Ant e Maven, ma per certi versi più moderno e flessibile. E‟ un progetto multipiattaforma e può essere scaricato dal sito di riferimento www.gradle.org. Trae vantaggio principalmente dall‟utilizzo di configurazioni svolte in un linguaggio dichiarativo: Groovy, un linguaggio di scripting che nasce in seno alla tecnologia Java.

gradle viene scaricato insieme ad Android Studio nella stessa versione di Android Studio (3.4.0 a maggio 2019). E‟ possibile aggiornare gradle a versioni più recenti anche senza aggiornare Android Studio, però la tecnica migliore è quella di aggiornare di tanto in tanto Android Studio alla versione più recente con automatico aggiornamento anche di gradle

La Programmazione

Note

Per visualizzare il Project Explorer -> Menù View / Tool Windows / Project

Prima dell‟onCreate possono essere dichiarate delle Property, ma non è consentito eseguire delle istruzioni (come ad esempio findViewById() )

Il nome delle risorse (layout, immagini, etc.) deve essere INTERAMENTE scritto in minuscolo.

Per terminare una Activity si può utilizzare il metodo finish();

Quando una classe è evidenziata in rosso significa che manca l‟import corrispondente. Digitando ALT + INVIO intelliJ aggiunge automaticamente in alto l’import necessario.

Digitando CTRL+ALT + SPAZIO su un metodo compare la sua firma

Se una Activity implementa una certa interfaccia che richiede l‟implementazione di un certo metodo è sufficiente fare ALT + INVIO sul nome dell‟interfaccia e poi scegliere Implementa Metodi

Page 9: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 9

Widget e View

Tutti i widget ereditano dalla superclasse View che è un astrazione di ciò che l'utente vedrà sullo

schermo. Per creare un widget personalizzato è sufficiente creare una classe che estenda la classe View o una sua sotto-classe.

Accesso ai controlli : La proprietà numerica id

Ogni widget deve avere come identificativo un nome che deve iniziare con il prefisso @+id/

Questo prefisso viene impostato in automatico nel momento in cui si trascina un widget sul layout.

Es: @+id/button1

Per accedere ai widget dall‟interno dell‟Activity si utilizza il metodo findViewById

final Button btn =(Button) findViewById(R.id.button1);

R.id.button1 è in realtà una costante numerica di tipo int

rappresentata solitamente in formato esadecimale

creata dal compilatore al momento della compilazione

salvata all‟interno del cosiddetto file R, che contiene non solo gli ID dei controlli, ma anche altri oggetti, ad esempio i puntatori alle immagini o le costanti definite all‟interno del file strings

R.id.button1 significa vai nella classe R, accedi all‟oggetto id e leggi il campo button1

Il metodo findViewById accede al widget del layout avente l‟ID numerico indicato.

Consente però di accedere soltanto ai controlli posizionati sul xmlLayout attualmente in uso.

Siccome il file R è unico a livello dell‟intera applicazione, nell‟applicazione non possono coesistere due widget aventi lo stesso name, anche appartenenti a Layout differenti

final utilizzato davanti al nome di una variabile significa sostanzialmente che la variabile non può

essere riassegnata. All‟interno del metodo onCreate devono essere dichiarate final tutti quei

riferimenti per i quali si intende gestire degli eventi o che sono utilizzati all‟interno degli eventi.

Infatti, quando la procedura onCreate termina, tutte le variabili locali vengono rimosse dallo stack, per cui non sono più accessibili al momento dell‟evento. Dichiarandole invece final, le variabili vengono

memorizzata all‟interno di uno string pool che rimane allocato anche al termine della procedura.

Visualizzazione della classe R

Il file R può essere aperto tramite il percorso indicato in figura : Project Files / app / app / build_generated / source / r_debug / com.example…. / R.java

Oppure digitando shift shift che apre una finestra di search in cui scrivere R.java e

selezionare l‟ultimo file visualizzato (ci sono più di 20 files R.java !)

E‟ una classe java autogenerata dal framework che serve a gestire tutte le risorse memorizzate dentro res (layout, etc).

All‟interno del file R.java il compilatore memorizza un puntatore numerico esadecimale per ogni elemento contenuto all‟interno del Layout.

Page 10: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 10

La Gestione degli eventi

Gli eventi sono gestiti tramite appositi oggetti detti listener.

1° soluzione

View.OnClickListener myListener = new View.OnClickListener() {

@Override

public void onClick(View v) { }

});

Nel momento in cui si digita View.OnClickListener myListener = new V

dopo un attimo compare l’intelliSense. Scegliendo la prima voce viene direttamente creato l’intero listener compreso il metodo onClick().

La classe View.OnClickListener è una classe astratta e non può essere direttamente istanziata

senza prima ridefinirne i metodi astratti. L'unico metodo astratto dichiarato all‟interno della classe è il metodo onClick che DEVE essere ridefinito all‟interno del listener.

Il metodo onClick riceve come unico parametro la View (il controllo) che ha generato l‟evento.

public void onClick(View v) {

Button btn =(Button) v;

if (btn.getText()=="Premi") {

btn.setText("Premuto");

btn.setBackgroundColor(Color.RED);

}

else {

btn.setText("Premi");

btn.setBackgroundColor(Color.rgb(214,215,215));

}

}

Per associare un listener di evento ad un controllo occorre accedere al controlo tramite findViewById e poi utilizare il metodo set seguito dal nome della classe di ascolto: setOnClickListener :

Button btn1=(Button) findViewById(R.id.btnPremi);

btn1.setOnClickListener(myListener);

Note La classe di ascolto, se definita esternamente rispetto al metodo set(), può essere scritta

o all‟interno del metodo onCreate (ma prima dell‟utilizzo del metodo set()), o all‟esterno del metodo onCreate (indifferentemente prima o dopo)

Un listener di evento può anche essere richiamato esplicitamente da codice, passandogli come parametro il puntatore al controllo opportuno (ad es in modo ricorsivo per reiniziare la procedura da capo).

2° soluzione

L‟associazione tra l‟oggetto ed il listener può essere eseguita anche sulla stessa riga nel modo seguente:

Button btn2=(Button) findViewById(R.id.btn2);

btn2.setOnClickListener(new View.OnClickListener() { } );

Page 11: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 11

3° Soluzione Associazione di una procedura di evento tramite il designer

Nel caso dell‟evento click, è sufficiente, fuori dal metodo onCreate(), definire una generica procedura con firma adeguata e poi assegnarla all‟evento direttamente tramite Designer cercando nella finestra delle Properties l‟evento onClick ed assegnandogli tramite menù a tendina il metodo desiderato.

public void btn3_click(View v) { }

Attenzione che :

Il metodo deve essere dichiarato public.

Questa tecnica funziona esclusivamente per l‟evento onClick, per il quale è stata definita una property aggiuntiva che consente di memorizzare il puntatore alla classe di gestione dell‟evento

Associazione di uno stesso evento a più controlli

La prima e la terza soluzione hanno il vantaggio di poter associare lo stesso listener a più controlli. All‟interno del listener si può utilizzare uno switch per individuare il controllo che ha scatenato l‟evento:

View.OnClickListener myListener = new View.OnClickListener() {

public void onClick(View v) {

switch(v.getId()){

case R.id.btn4:

txt.setText("E' stato premuto il pulsante 4");

break;

case R.id.btn5:

txt.setText("E' stato premuto il pulsante 5");

break;

}

}

};

btn1.setOnClickListener(myListener);

btn2.setOnClickListener(myListener);

4° Soluzione Implementazione dell’interfaccia onClickListener

Questa 4° soluzione rappresenta una semplificazione del caso 1 e rappresenta probabilmente la soluzione migliore per la gestione degli eventi in JAVA. Anziché definire all‟interno dell‟Activity corrente

una nuova classe View.OnClickListener, è possible fare in modo che la classe corrente

implementi l‟interfaccia View.OnClickListener. In questo modo è possibile ridefinire l‟evento onClick

direttamente all‟interno della classe corrente:

public class MainActivity extends AppCompatActivity

implements View.OnClickListener {

Gestore unico per tutti gli eventi click

public void onClick(View v){

switch(v.getId()){}

}

Button btn1 = (Button)findViewById(R.id.btn1);

btn1.setOnClickListener(this);

}

In caso di definizione di più listener, il successive maschera il precedente

Una Activity può implementare contemporaneamente PIU’ INTERFACCE separate da una virgola.

Page 12: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 12

Rilascio di un handler di evento

v.setEnabled(false);

In alternative si potrebbe scrivere :

v.setOnClickListener(null);

Con questa soluzione però l‟evento non viene generato, ma graficamente il click “sembra” attivo.

L’evento onTouch

Dal momento che i device non hanno il mouse, l‟evento click non è l‟evento migliore da utilizzare. Al posto dell‟evento onClick è possibile utilizzare l‟evento onTouch che presenta la seguente sintassi:

public boolean onTouch(View v, MotionEvent event) {

if( event.getAction() == MotionEvent.ACTION_DOWN)

((Button) v).setText(“Pressed”);

else if( event.getAction() == MotionEvent.ACTION_UP)

((Button) v).setText(“Premi”);

return true;

});

Il primo parametro rappresenta il controllo che ha generato l‟evento

Il secondo parametro rappresenta l‟azione specifica legata al Touch (ACTION_DOWN, ACTION_UP, ACTION_MOVE, etc.)

Se il metodo onTouch restituisce true significa che lo stesso Listener dovrà occuparsi anche di gestire i successivi eventi ACTION_MOVE e ACTION_UP ammesso che esista un handler

Se il metodo onTouch restituisce false significa che l‟evento viene consumato ed il Listener non sentirà più i successivi ACTION_MOVE e ACTION_UP

Generazione di numeri casuali in Java

Random rnd = new Random();

int n =rnd.nextInt(B-A+1) + A;

nextInt usato senza parametri genera unnumero compreso tra 0 e MAX_INT

Il tipo di un puntatore: instanceOf

InstanceOf verifica se un puntatore sta puntando ad un certo tipo di oggetto. E‟ l‟equivalente di IS in C#

if(v instanceof Button) { }

In alternativa si potrebbe utilizzare : String type = v.getClass().getName();

if(type.equalsIgnoreCase("android.widget.Button"))

Il concetto di Context

La classe Context è la classe base da cui, mediante più passaggi, ereditano le classi Activity.

Rappresenta una specie di interfaccia che consente all’Activity corrente di accedere a risorse e informazioni specifiche relative all’applicazione.

Mediante il context è possibile accedere ad un ampio insieme di caratteristiche e servizi a disposizione

dell‟applicazione. Il Context corrente viene utilizzato per :

accedere alle stringhe costanti memorizzate in strings.xml String s = context.getString(R.string.titolo);

Page 13: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 13

accedere alle eventuali variabili condivise dell‟applicazione:: SharedPreferences myPref = context.getSharedPreferences(name, mode);

quando si creano nuovi oggetti, per fare in modo che il nuovo oggetto possa anche lui accedere a risorse e informazioni relative all‟applicazione, occorre passare all‟oggetto il context corrente: TextView lbl = new TextView(context);

Visto che la classe Activity deriva dalla classe Context all‟interno del codice della classe Activity il

this rappresenta il Context dell‟applicazione.

Se però siamo all‟interno di un Listener di evento, this rappresenta il listener stesso.

Per accedere al Context, a seconda dei casi, si può utilizzare una delle seguenti sintassi:

this // istanza corrente

MyActivity.this; // Va bene quando mi trovo all‟interno di un listener di evento.

MyActivity. this è una proprietà che punta all‟istanza corrente

v.getContext(); // v è il puntatore ricevuto come parametro dal gestore di evento

getApplicationContext(); // equivalente a this

Le finestre di pop up : i Toast

Le finestre di pop up sono le cosiddette shallow window, cioè le finestre che appaiono / scompaiono con una durata temporanea paragonabile ad una notifica. Scopo di questo widget è quello di notificare all'utente un messaggio del tipo "salvataggio effettuato" oppure "operazione ok" in seguito a qualche operazione compiuta dall'utente .

Rispetto alle Alert Dialog il Toast è molto più semplice, non ha pulsanti di interazione e, dopo un breve tempo, scompare da solo senza che l‟utente debba eseguire un click.

Il costruttore della classe Toast si aspetta come unico parametro il Context di riferimento.

Toast t=new Toast(MainActivity.this);

t.makeText(getApplicationContext(), "Ok!!", Toast.LENGTH_SHORT);

t.show();

Nelle versioni più recenti la precedente sintassi non è più supportata ed è stata sostituita dall‟utilizzo del metodo statico Toast.makeText che presenta 3 parametri che sono :

1. Il Context dell‟applicazione nel quale applicare il toast. Dato che il codice è inserito all'interno del listener non possiamo passare come contesto this in quanto passeremmo come argomento il listener stesso al quale non è applicabile un toast. Occorre pertanto utilizzare

getApplicationContext()oppure MyActivity.this

2. Il messaggio da visualizzare 3. La durata della visualizzazione (LONG oppure SHORT)

Toast t =Toast.makeText(getApplicationContext(),”ok”, Toast.LENGTH_SHORT)

t.show();

E‟ anche possibile aggiungere il testo in un secondo momento, dopo aver istanziato l‟oggetto:

Toast t =Toast.makeText(this, ”ciao”, Toast.LENGTH_SHORT)

t.setText(“myMessage”); t.setGravity(Gravity.CENTER, 0, 0);

t.show();

Il metodo setGravity() definisce la posizione dello schermo in cui visualizzare il pop up.

Il valore 0,0 rappresenta l‟offset di visualizzazione rispetto al valore impostato da Gravity

Page 14: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 14

Utilizzo del toast per la visualizzazione di una immagine

Un toast non può visualizzare contemporaneamente Testo e Immagine. O il testo, o l‟immagine. Nel caso si voglia visualizzare una immagine, occorre per prima cosa caricare l‟immagine da utilizzare.

ImageView img = new ImageView(this);

img.setImageResource(R.drawable.ic_launcher);

Dopo di che al posto del metodo t.makeText() occorre utilizzare il metodo t.setView()

Toast t = new Toast(getApplicationContext());

t.setView(img);

t.show();

AlertDialog

Widget utilizzato ticamente per richiedere una richiesta di conferma da parte dell'utente per una qualche operazione. Per esempio un'applicazione che richiede una connessione ad Internet, potrebbe mostrare un'AlertDialog all'utente nel caso in cui il dispositivo non si riesca a collegarsi ad Internet.

AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setTitle("AlertDialog");

builder.setMessage("E' stato premuto Pulsante 3");

builder.setIcon(R.mipmap. ic_launcher);

Aggiunta dei pulsanti di dialogo

Le dialog vengono automaticamente chiuse in corrispondenza del click su uno dei pulsanti di dialogo. Se non vengono inseriti pulsanti di dialogo, per chiudere la AlertDialog occorre clickare nell‟area dell‟Activity. I pulsanti di dialogo utilizzabili sono 3 (tutti con posizione fissa)

PositiveButton posizionato nella parte destra della finestra

NegativeButton posizionato nella parte centrale della finestra

NeutralButton posizionato nella parte sinistra della finestra

builder.setPositiveButton("Si", new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int id) {

lbl.setText("cliccato il tasto SI");

dialog.dismiss(); // oppure dialog.cancel();

}

});

builder.setNegativeButton("No", new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int id) {

lbl.setText("cliccato il tasto NO");

}

});

builder.setNeutralButton("Annulla",new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int id) {

lbl.setText("cliccato il tasto Annulla");

}

});

Il primo parametro indica il testo da visualizzare all‟interno del pulsante

Il secondo parametro indica un oggetto Listener di tipo DialogInterface.OnClickListener

Anche nel caso del DialogInterface.OnClickListener occorre comunque ridefinire il metodo OnClick ed inserire all‟interno il codice relativo al comportamento che vogliamo dare al bottone.

Il metodo dialog.dismiss() chiude la finestra corrente. La finestra si chiude automaticamente in

corrispondenza del click su uno dei buttons. dismiss() consente la chiusura forzata da codice.

Page 15: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 15

builder.setCancelable(true/false); il false ha l'effetto di disabilitare il tasto back del

telefono e dunque l'utente dovrà necessariamente cliccare su uno dei bottoni per chiudere la Dialog che quindi diventa modale.

al termine: AlertDialog dlg = builder.create();

dlg.show();

// oppure builder.show();

Utilizzo di un unico Listener

La classe DialogInterface.OnClickListener utilizzata negli esempi precedenti dispone di un unico evento onClick il quale si aspetta due parametri:

1. un oggetto di tipo AlertDialog che indica sostanzialmente il sender (quale dialog ha generato l'evento. Attenzione AlertDialog, non Builder. Occorre pertanto utilizzare il builder.create

2. un id che identifica il bottone premuto all‟interno della dialog

Questi due parametri consentono di utilizzare un unico listener per tutte le AlertDialog dell‟applicazione, scritto esternamente rispetto alle Dialog medesime. Il codice interno dovrà semplicemente eseguire un primo switch sul parametro dialog (identificando quale AlertDialog ha attivato il listener) con all‟interno un altro switch per capire quale bottone, relativo alla AlertDialog presa in esame, è stato premuto. DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener(){

public void onClick(DialogInterface dialog, int id) {

String text = "";

if (dialog == dlg2) {

switch (id) {

case DialogInterface.BUTTON_POSITIVE: // -1

text = "OK";

case DialogInterface.BUTTON_NEGATIVE: // -2

text = "No";

case DialogInterface.BUTTON_NEUTRAL: // -3

text = "Cancel";

}

Toast.makeText(this, text, Toast.LENGTH_LONG).show();

}

}

}

Input Dialog

Per trasformare una AlertDialog in InputDialog è sufficiente aggiungere una casella di input

all‟interno della Dialog stessa.

AlertDialog.Builder dlg = new AlertDialog.Builder(this);

dlg.setTitle("InputDialog");

dlg.setMessage("Inserire il nome:");

dlg.setIcon(R.drawable. ic_launcher);

final EditText input = new EditText(this);

input.setInputType(InputType.TYPE_CLASS_TEXT);

dlg.setView(input);

Page 16: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 16

dlg.setPositiveButton("OK", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int id) {

Toast.show(input.getText().toString());

}

});

dlg.show()

Attenzione che se il programma necessita di aprire più finestre di dialogo consecutive utilizzando un codice del tipo :

apriFinestra3();

apriFinestra2();

apriFinestra1();

le tre finestre verranno aperte consecutivamente una sopra l‟altra, per cui occorrerà richiamarle in ordine inverso rispetto alla sequenza desiderata di apertura. L‟evento click di ogni pulsante memorizzarà il

valore inserito all‟interno di una apposita variabile globale. Il click di finestra3 richiamrerà una funzione

elabora() che andrà ad elaborare i tre valori inseriti.

Utilizzo dei principali Controlli

La casella di testo : il widget PlainText (EditText)

La proprietà inputType consente di definire il comportamento della Casella di Testo :

inputType = "none" qualsiasi carattere

inputType = "text" solo caratteri testuali, compresa la punteggiatura. No che accentati

inputType = "number" solo numeri. In corrispondenza del click si apre il tasterino numerico

inputType = "password" visualizza asterischi

hint = "placeholder" suggerimento iniziale. Simile al placeholder html

android:backgroundTint="@android:color/black" // Colore della linea di sfondo

Nelle versioni più recenti sono stati aggiunti molti TextFields specifici con inputType già settato.

Quando l'utente clicca su un oggetto di tipo EditText appare automaticamente una tastiera virtuale sullo schermo del dispositivo che permette l'immissione del testo.

La chiusura della tastiera, quando l'utente termina l'immissione del testo, non è invece implementata direttamente nella tastiera, ma occorre aggiungere un bottone particolare che consenta di rimuovere la tastiera dallo schermo. Per arricchire la tastiera con il tasto done occorre inserire la seguente riga:

txt.setImeOptions(EditorInfo.IME_ACTION_DONE);

Il metodo setImeOptions ha l'effetto di settare un IME nella tastiera.

Un IME (input method) implementa un particolare modello di interazione che l'utente può utilizzare. Il tipo di IME da aggiungere alla tastiera deve essere passato come argomento al metodo setImeOptions. I principali tipi IME (mutuamente esclusivi) sono:

IME_ACTION_DONE

IME_ACTION_GO Nel caso di campi URL invia la richiesta HTTP al server

IME_ACTION_NEXT Passa al campo successivo

IME_ACTION_SEND Invia il testo ad un servizio specifico

Page 17: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 17

Per cambiare il colore del cursore (how to change cursor color in edittext in android studio) occorre creare all‟interno della cartella drawable un file myCursor.xml contenente il seguente codice:

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"

android:shape="rectangle" >

<size android:width="1dp" />

<solid android:color="#000000"/>

</shape>

Dopo di che all‟interno del layout utilizzare la segeunte riga: android:textCursorDrawable="@drawable/black_cursor"

Check Box e Radio Button

A fianco di ogni Check Box / Radio Button viene sempre visualizzato un TextView contenente un testo associato al controllo. Non esiste il concetto di value I Radio Button sono mutuamente esclusivi soltanto se inseriti all‟interno di un medesimo RadioGroup (avente ad esempio id = optGroup). Non sono mutuamente esclusivi i Radio Button inseriti

direttamente all‟interno di un Layout o altro. Esempi di utilizzo:

if (myOpt.isChecked()) {

myOpt.setText(“checked”);

myOpt.setChecked(false); }

int id_elemSelez = optGroup.getCheckedRadioButtonId();

RadioButton opt = (RadioButton) findViewById(id);

RadioButton opt = (RadioButton) optGroup.getChildAt(2); // 3° rdb

for(int i=0; i< chkGroup.getChildCount(); i++) {

CheckBox chk = (CheckBox) chkGroup.getChildAt(i);

if (chk.isChecked()) aus += chk.getText() + ", "; }

In corrispondeza del click si può utilizzare il seguente evento :

optBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {

public void onCheckedChanged(CompoundButton v, boolean isChecked) {

if (isChecked) { }

}

La gestione delle immagini : il widget ImageView

Consente di visualizzare un'immagine memorizzata all‟interno della cartella res / mipmap

Nel caso di immagini di dimensioni superiori alla risoluzione del controllo, Android effettua un resize automatico in modo da renderla completamente visibile.

Aggiungere una immagine nella cartella xhdpi

Così come per i Layout, anche per le res i nomi dei files devono essere scritto in minuscolo.

Impostazione dell‟immagine sul Layout Grafico. Trascinare un oggetto ImageView : android:src="@mipmap/desert" // senza estensione !!

Page 18: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 18

Impostazione dell‟immagine da codice : final ImageView img =(ImageView) findViewById(R.id.img1);

img.setImageResource(R.mipmap.desert); // senza estensione !!

img.setImageBitmap(bitmap); dove bitmap può essere letto da file o ricevuto da un server

il metodo getResources().getIdentifier()

Consente di eseguire un accesso diretto a tutte le risorse identificate tramite il file R, come ad esempio

Le risorse memorizzata all‟interno della cartella res (es R.mipmap.desert)

I controlli identificati tramite un ID statico (es R.id.btn1)

Sia R.drawable.desert che R.id.btn1 sono costanti esadecimali, per cui NON possono essere

utilizzate all‟interno di un ciclo per accedere ad un elenco di oggetti con nomi simili (come ad esempio img1, img2, img,3, btn1, btn2, btn3, etc).

Il metodo this.getResources() consente di accedere a tutte le risorse della app. Partendo

dall‟insieme delle risorse, il metodo getIdentifier() consente di ottenere l‟ID esadecimale di una

singola risorsa a partire dalla stringa identificativa (ad esempio “btn1” oppure “img25”

Esempio 1: accesso diretto all’identificativo di una immagine

Resources res = this.getResources()

int id1 =res.getIdentifier(“desert”, "mipmap", this.getPackageName());

int id2 =res.getIdentifier(“btn”+i, "id", this.getPackageName());

Restituisce l‟ID numerico dell‟oggetto cercato, oppure 0 se non lo trova. Esempio 2: ciclo di caricamento di immagini aventi nomi consecutivi

getIdentifier() può essere utilizzato ad esempio par accedere a tutte le immagini contenute

all‟nterno della cartella mipmap e caricare i relativi ID all‟interno di un ArrayList:

ArrayList<Integer> listaImmagini = new ArrayList<Integer>();

int id;

int i=1;

do {

id = getResources().getIdentifier("img" + i, "mipmap", this.getPackageName());

if(id!=0) listaImmagini.add(id);

i++;

} while(id!=0);

Nota

In realtà le costanti esadecimali di identificazione delle immagini sono numeri positivi crescenti (con incremento di 1) sulla base delle immagini ordinate in ordine alfabetico. Per cui in realtà è possibile accedere in sequenza alle varie immagini nel modo seguente:

img.setImageResource(R.drawable.aaa + i);

dove aaa è il nome della prima immagine, mentre R.drawable.aaa è il suo codice esadecimale.

Page 19: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 19

Creazione Dinamica dei controlli

Il layout di una Activity può essere creato dinamicamente da codice anche senza l‟ausilio di un file xml.

LinearLayout layout = new LinearLayout(this);

setContentView(layout);

int id=1;

Button btn = new Button (this);

btn.setText("Bottone 1");

btn.setId(id);

layout.addView(btn);

id++;

btn = new Button (this);

btn.setText("Bottone 2");

btn.setId(id);

layout.addView(btn);

Note sulla gestione dell’Id

L‟Id è semplicemente un numero intero esadecimale utilizzato per identificare ciascun controllo (View). L‟Id può essere assegnato staticamente all‟interno del file XML oppure dinamicamente da codice. Assegnazione statica dell’ID all’interno del layout

android:id=”@+id/button1”

Per i controlli definiti staticamente, il valore dell‟id deve essere univoco sull‟intera applicazione. Il compilatore trasforma questa stringa alfanumerica in un intero positivo esadecimale univoco.

Per accedere da codice al controllo si utilizza il metodo findViewById : Button btn =(Button) findViewById(R.id.button1);

Il metodo v.getId() esegue l‟operazione invesa di findViewById(), cioè restituisce la costante

esadecimale del controllo a partire dal riferimento

if( v.getId() == R.id.button1)

Assegnazione dinamica dell’ID tramite codice

L‟assegnazione può essere eseguita semplicemente mediate il metodo .setId(int) dove il parametro

deve essere un numero > 0 non necessariamente univoco e che può anche essere in conflitto con un id statico. L‟univocità dell‟ID è importante soltanto dal punto di vista logico-funzionale. Lo 0 ha il significato di risorsa non trovata (getIdentifier() restituisce 0 se non trova la risorsa)

setId() si aspetta un oggetto ID, mentre getId() restituisce un oggetto Id che è sostanzialmente un intero unsigned. La conseguenza a livello operativo è che, insieme all‟ID, nel caso di assegnazioni, letture, confronti, non è MAI possibile utilizzare un numero diretto ma occorre SEMPRE passare attraverso una variabile:

int aus = 1;

btn1.setId(aus);

int id = v.getId();

if(id == aus) .......

Page 20: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 20

In alternativa si può definire all‟interno della cartella res/values un file denominato ids.xml <?xml version="1.0" encoding="utf-8"?>

<resources>

<item type="id" name="btn1" />

<item type="id" name="btn2" />

</resources>

btn.setId(R.id.btn1);

L‟API 17 ha introdotto il metodo View.generateViewId() che consente di generare ID univoci.

Riferimento ad un controllo creato dinamicamente

Dopo aver creato il controllo e aggiunto al layout, l‟accesso può essere eseguito nel solito modo: Button btn =(Button) findViewById(id);

Come è possible consentire id non univoci ?

Il fatto che l‟ID non debba necessariamente essere univoco si spiega con il fatto che, quando si ricerca un controllo tramite findViewById, esso esegue una ricerca sequenziale restituendo il primo controllo

avente l‟ID indicato. Poiché nella lista dei controlli quelli creati staticamente a Design Time si trovano prima di quelli creati dinamicamente, findViewById restituirà sempre il controllo statico.

List<Object> e ArrayList<Object>

List è una interfaccia,e dunque non può essere istanziata direttamente, ma può ad esempio essere

restituita da un metodo che esegue il cast da ArrayList. ArrayList viceversa è una classe che

implementa l‟interfaccia List e dunque può essere istanziata. Ci sono anche altre classi che implementano l‟interfaccia List in modo diverso da ArrayList, ad es la classe LinkedList.

// Creazione dell’ArrayList (<String> è il default e può anche essere omesso)

ArrayList<String> list = new ArrayList<String>();

// Popolamento dell’ArrayList

list.add("Fossano");

list.add(pos, "Saluzzo"); // int pos = 0;

list.addAll(list2);

String[] vect = new String[] { "Cuneo", "Alba", "Genola", "Bra" };

list.addAll(Arrays.asList(vect));

// Accesso agli elementi dell’ArrayList

for (int i = 0; i<list.size(); i++)

msg += list.get(i) + "\r\n";

if(list.contains("Fossano"));

// Scansione dell’ArrayList tramite ciclo for each

for(String s : list){

msg += s + "\r\n";

// Modifica di un elemento esistente all’interno dell’ArrayList

list.set(i, "Torino")

Il metodo set è utilizzabile anche nel caso di ArrayList<Object>

list.set(i, studente)

// Cancellazione di elementi

list.remove(pos); list.clear();

Page 21: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 21

Ordinamento di un arrayList

Collections.sort(arrayList);

Nel caso di ArrayList di Oggetti, ocorre definire all‟interno della classe un metodo che definisca quale campo utilizzare per i confronti e come eseguire i confronti (crescente o decrescente).

1° soluzione : L’interfaccia Comparable

Sulla classe di appartenenza degli oggetti si può implementare l‟interfaccia Comparable con il metodo compareTo() in cui si definisce il campo su cui eseguire il confronto. Tenere presente che in java una classe può implementare più interfacce separate da virgola.

public class Student implements Comparable<Student> {

private String studentName;

private int studentAge;

public Student(String studentName, int studentAge) {

this.studentName = studentName;

this.studentAge = studentAge;

}

@Override

public int compareTo(Student student) {

int compareAge=student.getStudentAge();

/* For Ascending order */

return this.getStudentAge() - compareAge;

/* For Descending order */

//return compareAge-this.getStudentAge();

}

Notare che compareTo restituisce sempre un int che ha il seguente significato:

0 : l‟elemento corrente ha lo stesso valore dell‟elemento confrontato >0 : l‟elemento corrente è maggiore (numericamente o alfabeticamente) dell‟elemento confrontato, che

dunque viene fatto salire <0 : l‟elemento corrente è minore dell‟elemento confrontato, che dunque viene fatto scendere

2° soluzione : La classe Comparator<>

Una seconda soluzione consiste nel dotare la classe di appartenenza degli oggetti della classe Comparator<Studente> la quale utilizza un metodo compare() per eseguire i confronti:

public static Comparator<Studente> confronta = new Comparator<Studente>() {

public int compare(Studente s1, Studente s2) {

String name1 = s1.getNome().toUpperCase();

String name2 = s2.getNome().toUpperCase();

//ascending order

return name1.compareTo(name2);

//descending order

//return name2.compareTo(name1);

}

};

Il chiamante dovrà indicare la classe di confronto all‟interno del secondo parametro. Collections.sort(arrayList, Persona.confronta);

Page 22: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 22

La scalatura delle immagini PX vs DP

L‟unità px rappresenta I pixel reali dello schermo. Spesso però gli schermi degli smartphone hanno densità differenti, deve per densità si intende il PPI, numero di pixel per pollice lineare.

Due smartphone con uno stesso schermo da 5” possono evere risoluzione diversa, ad esempio il secondo pari al doppio del primo. Questo significa che il secondo smartphone ha una densità di pixel doppia rispetto al primo, cioè i pixel sono molto più vicini tra loro. Cioè il secondo smartphone ha un PPI doppio e quindi può visualizzare il doppio delle cose, ciacuna con dimensioni dimezzate. Un oggetto con larghezza 200px si vedrebbe sempre più piccolo su smartphone aventi PPI crescenti.

Quando si progetta il layout di una app si tende ad assegnare ai vari oggetti una dimensione fissa, indipendente dal PPI del dispositivo. Questo risultato lo si ottiene con l‟introduzione di una nuova unità di misura: il DP Density-independent Pixels. Si supponga di voler visualizzare una immagine con dimensione ½ pollice x ½ pollice.

Su un dipositivo con PPI = 120 l‟immagine dovrà estendersi per 60 x 60 pixel

Su un dipositivo con PPI = 240 l‟immagine dovrà estendersi per 120 x 120 pixel, cioè dovrà essere il doppio rispetto all‟immagine precedente.

mdpi

hdpi

xhdpi

Page 23: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 23

Density Bucket

Poiché è impensabile memorizzare immagini diverse per ogni possibile PPI del dispositivo, sono stati definiti degli appositi Density Bucket (secchielli di densità):

Density Bucket name DPI from…. to…… density

ldpi low 120 dpi 100 140 0,75

mdpi medium 160 dpi 140 180 1

hdpi height 240 dpi 180 280 1,5

xhdpi extra height 320 dpi 280 360 2

xxhdpi extra extra height 480 dpi 400 3

Per ognuno di questi Density Bucket è stata predisposta una apposita cartella all‟interno delle risorse. Per poter gestire l‟imagine precedente con dimensioni fisse di ½ pollice x ½ pollice, occorre creare cinque immagini con grandezza differente salvandole nelle apposite cartelle. Le varie immagini dovranno avere le dimensioni indicate nel seguente prospetto:

A seconda del PPI del dispositivo la app visualizzerà l‟immagine opportuna. E’ anche possibile memorizzare una sola immagine (in genere quella con risoluzione maggiore). In caso di assenza delle immagini con risoluzione diversa, la app provvederà automaticamente a riscalare l‟immagine espressa in dp in modo da ottere le dimensioni opportune. Funzioni per la conversione da px a dp

Anche impostando tutte le dimensioni in DP, molte funzioni relative al layout restituiscono o si aspettano le dimensioni degli oggetti in px, per cui per poterle riassegnare in dp occorre eseguire una conversione manule da dp a px, utilizzando ad esempio il seguente metodo:

context.getResources().getDisplayMetrics().density;

che restituisce il valore density del dispositivo, come indicato nell‟ultima colonna della tabella precedente

Per semplificarne la gestione si possono predisporre le seguenti funzioni:

public int dpToPx(int dp) {

float density = this.getResources().getDisplayMetrics().density;

return Math.round((float) dp * density);

}

public int pxToDp(int px) {

float density = this.getResources().getDisplayMetrics().density;

return Math.round((float) px / density);

}

Page 24: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 24

Principali Proprietà Comuni

I valori match_parent (equivalente a fill_parent deprecato) e” wrap_content” sono

sostanzialmente delle contanti predefinite che valgono rispettivamente 0 e -2

text = “testo del controllo”. Utilizza istanze implicite, per cui va bene eseguire il confronto con ==

tag = Campo nascosto di tipo object molto comodo per salvare valori di appoggio (id record)

Utilizza istanze eplicite per cui occorre necessariamente utilizzare .equals()

textSize = "18sp" // Gli sp sono analoghi ai dp ma riferiti al font textStyle = "bold|italic"

typeFace = famiglia del font

width e height = rappresentanto le dimensioni correnti del controllo espresse come numero intero

(in pixel) senza unità di misura. Viceversa layout_width e layout_height accettano come

parametro una stringa che può essere ad esempio "185dp” oppure un valore predefinito come

“match_parent” o “wrap_content”,

Notare che sia width che layout_width non possono essere lette in corrispondenza

dell‟onCreate in quanto all‟onCreate il rendering della pagina non è ancora stato terminato.

La stessa cosa vale per gli eventi successi onStart() e onResume(). Per leggere i valori di width e heigth in corrispondenza dell‟onCreate è comunque disponibile un metodo .measure con le

property measuredWidth e measuredHeight (per i quali si rimanda alla documentazione)

padding = "10dp" // Il padding non viene aggiunto a width e height come in html, ma provoca

semplicemente uno spostamento del testo. Se ad esempio si imposta un allineamento del testo a sinistra, conviene dare un piccolo paddingLeft. In questo caso il paddingRight non viene „sentito‟. In caso di allineamento centrato, si può dare un paddingLeft negativo per spostare il testo verso sinistra ed un paddingTop positivo per abbassare un pochino il testo.

enabled = true / false // Disabilita il controllo

editable = true / false // Usato nei TextView

visibility = visible / unvisible

textColor = "#ffffff00" // colore del testo giallo. // Il primo FF è l‟opacity. FF=solido,

background = "#ff0000ff" // colore di sfondo blu (solido) background = "@android:color/holo_blue_dark" // layout

gravity = ancoraggio del testo rispetto alle dimensioni dell‟oggetto.

Funziona „SOLO „ se sull‟oggettos ono state impostate WIDTH e HEIGHT Ad esempio un TextView può avere larghezza 200dp ma un testo interno molto breve che può essere allineato nel modo seguente:

LEFT / RIGHT a sinistra / a destra TOP / BOTTOM in cima al contenitore / in fondo al contenitore CENTER Al centro del contenitore, rispetto sia all‟asse X che all‟asse Y

Questi valori possono essere combinati tra loro tramite una OR bit a bit.

Es Gravity.LEFT | Gravity.BOTTOM

Page 25: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 25

Impostazione tramite codice

Per l‟accesso da codice alle Property occorre utilizzare i metodi get e set che come unità di misura utilizzano SOLTANTO i pixel, per cui spesso occorre eseguire manualmente le conversioni necessarie.

.getText(); restituisce il testo del widget. Spesso occorre fare .toString()

.setText(“testo”); setta il testo del widget

.setTextSize(16); imposta la dimensione del testo in sp

.setTypeface(Typeface.SANS_SERIF); // font-style

.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD); // font-style + bold

.setTypeface(null, Typeface.BOLD); // come font-style usa il default

.setTypeface(null, Typeface.ITALIC);

.setTypeface(null, Typeface.NORMAL);

.setTypeface(null, Typeface.BOLD_ITALIC);

.setTypeface(v.getTypeface(), Typeface.BOLD_ITALIC); // get current TypeFace

.setPadding(6,6,6,6); I valori di Padding e Margin partono da LEFT / TOP / RIGHT / BOTTOM

.getPaddingLeft();

.setGravity(Gravity.CENTER_VERTICAL); // Allineamento del testo

Il metodo .setGravity(Gravity.LEFT | Gravity.BOTTOM), oltre al valore di gravity,

presenta due altri parametri che rappresentano offsetX e offetY rispetto alla posizione del 1 param.

.setEnabled(true);

.setEditable(false);

.setVisibility(View.INVISIBLE);

.setTextColor(Color.RED);

.setBackgroundColor(Color.GREEN);

.bringToFront() porta il widget corrente in primo piano.

I Layout

Tutte le proprietà che interagiscono con il layout (cioè width, height, margin, gravity del controllo rispetto al layout, etc.), iniziano con la parola layout. Queste proprietà dipendono dal tipo di layout utilizzato. I principali layout disponibili sono 4:

Linear Layout

Relative Layout (da cui deriva il Constraint Layout)

Table Layout

Frame Layout

Page 26: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 26

1. LinearLayout

Era inizialmente il layout di default. Dispone gli elementi uno di seguito all'altro.

La proprietà orientation (vertical o horizontal=defaut) consente di impostare il layout:

verticale - gli elementi vengono disposti uno sotto l'altro (uno per riga)

orizzontale - gli elementi vengono disposti uno dopo l'altro da sinistra verso destra (tutti sulla

stessa riga). In questo caso i vari elementi dovranno avere width="wrap_content" e NON

width="match_parent" altrimenti si vedrebbe un solo controllo.

In entrambi i casi gli elementi vengono aggiunti alla finestra finchè c'è spazio . Se si richiede l'inserimento di un ulteriore elemento, android cerca di ridimensionarlo (peraltro in modo molto poco leggibile) per inserirlo comunque all'interno dello schermo. Questo comportamento è limitato ad una soglia oltre la quale, semplicemente, gli elementi aggiuntivi non vengono più visualizzati.

La proprietà gravity del LinearLayout consente di definire l‟allineamento dei widget interni.

La proprietà layoutDirection consente visualizzare i controlli interni da sinistra verso destra

(LAYOUT_DIRECTION_LTR) oppure da destra verso sinistra (LAYOUT_DIRECTION_RTL)

layout.setOrientation(LinearLayout.HORIZONTAL);

layout.setGravity(Gravity.TOP);

Allineamento degli oggetti interni

layout_width="185dp” // larghezza del controllo

layout_height="wrap_content" // altezza del controllo adattata al contenuto

layout_margin=”10dp” // margini esterni.

layout_gravity // ancoraggio degli oggetti interni rispetto al Layout.

layout_weight=1; // copertura dell‟area libera in un linear layout orizzonale.

Attenzione che la propriatà gravity, vale SOLO ed ESCLUSIVAMENTE per i testi per cui non può essere utilizzata, ad esempio, per gli imageView. Nel caso di check box / radio button provoca l‟allineamento del testi ma NON l‟allineamento del quadratino / cerchietto rispetto al layout

Il valore layout_gravity =Gravity.Right ha senso ad esempio nel caso di un

Linear Layout verticale in cui l‟oggetto occupa tutta la riga. Se l‟oggetto corrente ha dimensioni ridotte, impostando layout_gravity =Gravity.Right l‟oggetto viene ancorato sul lato destro

del contenitore lasciando libero lo spazio antecedente

Nel caso invece di un Linear Layout orizzontale in cui gli oggetti i susseguono uno dopo l‟altro, l‟imposatzione layout_gravity =Gravity.Right applicata ad esempio all‟ultimo controllo non

sortisce nessun effetto perché si verrebbe a creare davanti all‟oggetto uno spazio vuoto che il designer non sa dove posizionare.

Nel caso di un layout orizzontale, la proprietà layout-weight=1 consente di definire quale peso

avrà il controllo nella ridistribuzione degli spazi liberi all‟interno della riga. Il valore di default è 0. Se si assegna il valore 1, il controllo coprirà l‟intera area libera della riga. A tal fine è però necessario che il contenitore abbia una width pari a MatchParent. Sull‟elemento occorre poi impostare il gravity del testo interno rispetto all‟elemento, in modo da allineare il testo a sinistra rispetto al controllo (con la spaziatura a destra) oppure a destra (con la spaziatura a sinistra).

Riguardo al width del controllo su cui si imposta layout-weight=1 si può utilizzare qualsiasi

valore intanto la larghezza viene poi calcolata automaticamente sulla base dell‟area libera. Normalmente è consigliato il valore 0.

Volendo ridistribuire l‟area libera in modo proporzionale si può impostare sul layout ad esempio weightSum=5, sul primo controllo weight=1, sul secondo weight=3, sul terzo weight=1

Page 27: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 27

Impostazione da codice

Le propreità di tipo layout devono essere definite tramite un oggetto LayoutParams ed assegnate al

controlo tramite il metodo .setLayoutParams(); L’oggetto LayoutParams deve essere del tipo

specifico a seconda del layout a cui il controllo appartiene.

L‟oggetto layoutParams può essere istanziato ex-novo oppure può essere ricavato da un qualunque

controllo del layout corrente tramite il metodo .getLayoutParams(); riassegnando poi soltanto le

singole proprietà desiderate Per le propery numeriche occorre omettere l’unità di misura che viene automaticamente interpretata in pixel per le dimensioni spaziali e sp per i testi.

LinearLayout.LayoutParams params = (cast)v.getLayoutParams();

params.width=200;

v.setLayoutParams(params);

Nel caso creazione di una istanza ex-novo, occorre passare al costruttore i valori di width e height : (solo in alcuni casi (es TableRow) è ammesso un costruttore privo i parametri).

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(300,

LinearLayout.LayoutParams.WRAP_CONTENT);

params.width=50; // sovrascrive il 300 passato al costruttore

params.setMargins(6,6,6,6);

params.setMarginLeft(6);

params.gravity= Gravity.CENTER_VERTICAL;

params.weight=1.0f; // oppure =1; sembra indifferente

btn.setLayoutParams(params);

// oppure, nel caso si vogliano impostare soltanto width e height, si può semplicemente fare: btn.setLayoutParams(new LinearLayout.LayoutParams (300, 400);

Nel caso in cui interessi soltanto la lettura dei valori si può utilizare la seguente forma abbreviata int w = v.getLayoutParams().width;

oppure semplicemente int w = v.getWidth(); // NON è consentito eseguire il set() ??

2a. RelativeLayout

Layout di default dalla versione 5 in avanti. Consente di posizionare liberamente gli elementi indicando dimensione e posizione. Invece dei valori assoluti è possibile gestire l‟allineamento di un elemento rispetto agli elementi circostanti. La proprietà Layout_Gravity non è riconosciuta. Esempi :

Il primo elementi potrà avere:

android:layout_alignParentLeft="true"

android:layout_alignParentTop="true"

Il secondo elemento potrà, ad esempio, essere posizionato sotto il primo:

android:layout_below="@+id/idElementoPrecedente "

android:layout_centerHorizontal="true"

Il terzo elemento potrà essere posizionato a fianco del secondo:

android:layout_toRightOf="@+id/idElementoPrecedente"

Il quarto elemento potrà essere centrato nella pagina:

android:layout_centerHorizontal="true"

android:layout_centerVertical="true"

Page 28: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 28

Impostazione da codice

Le proprietà precedenti, disponibili soltanto nel caso del RelativeLayout, sono dette rules e sono impostabili da codice nel modo seguente:

RelativeLayout.LayoutParams params = (cast) v.getLayoutParams();

Se però l‟oggetto è stato creato dinamicamente v.getLayoutParams()restituice null.

Occorre allora istanziarlo ex-novo passandogli al solito width e height

RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(

RelativeLayout.LayoutParams.WRAP_CONTENT,

RelativeLayout.LayoutParams.WRAP_CONTENT);

params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);

params.addRule(RelativeLayout.ALIGN_PARENT_TOP);

params.addRule(RelativeLayout.BELOW, R.id.previous);

params.addRule(RelativeLayout.RIGHT_OF, R.id.previous);

params.addRule(RelativeLayout.CENTER_HORIZONTAL);

params.addRule(RelativeLayout.CENTER_VERTICAL);

btn.setLayoutParams(params);

Pr quanto riguarda la rimozione di una regola, esistono due sintassi, la prima antecedente alla versione 17 ma ancora oggi supportata, la seconda disponibile dalla versione 17 in avanti:

params.addRule(RelativeLayout.CENTER_HORIZONTAL, 0);

params.removeRule(RelativeLayout.CENTER_HORIZONTAL);

I metodi addRule() e removeRule() non valgono per il constraintLayout.

2b. Constraint Layout

Evoluzione del Relative Layout con alcune nuove property che rendono ancora più semplice ed immediato il posizionamento degli oggetti.

Ogni oggetto può essere ancorato ai 4 oggetti intorno ed è necessario definire almeno 2 ancore.

Non è possibile cancellare una sola ancora. Per cancellare tutte le ancore dell‟oggetto selezionare l‟oggetto ed utilizzare l‟apposito puslantino di cancellazione delle ancore

Attenzione che gli oggetti ancorati sulla parte inferiore del layout, quando compare la tastiera di inserimento caratteri, vengono traslati in alto al di sopra della tastier, sovrapponendosi ai controlli presenti in quella posizione. Quando si usa la tastiera di insermento evitare la l‟ancora a fine pagina

Principali Proprietà di posizionamento

app:layout_constraintLeft_toLeftOf=”parent”Lato sx ancorato al lato sx del genitore

app:layout_constraintLeft_toRightOf=”ctrl” Lato sx ancorato al lato dx di un altro controllo

app:layout_constraintTop_toTopOf=”parent” Lato sup ancorato al lato sup del genitore

app:layout_constraintTop_toBottomOf=”ctrl” Lato sup ancorato al lato inf di un altro control

In caso di flusso da sinistra verso destra Start coincide con Left, End coincide con Right

In caso di flusso da destra verso sinistra Start coincide con Right, End coincide con Left

Per il Constraint Layout, riguardo alle property layout_width e layout_height, non è ammesso il

valore match_parent. Al suo posto si utilizza match_constraint chef a sì che il controllo si estenda

fino ai limiti del constraint. Per riempire l‟intera area client si può impostare match_constraint sia su

width sia su height e settare il constraint sinistro ed il constraint destro al valore parent (come nel codice che segue):

Page 29: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 29

<TextView

android:layout_width="0dp"

android:layout_height="wrap_content"

app:layout_constraintLeft_toLeftOf="parent"

app:layout_constraintRight_toRightOf="parent"/>

La proprietà constraintHorizontal_bias

Questa propreità contiene un numero float compreso tra 0 e 1 che, se impostata a 0.5, esegue una centratura orizzontale dell‟oggetto. Il valore 0.5 rappresenta il valore di default, per cui quando si imposta questo valore la property viene rimossa dal file xml. Il valore 0f visualizzza l‟oggetto tutto a sinistra (a meno dell‟impostazione di un marginLeft) Il valore 1f visualizzza l‟oggetto tutto a destra (a meno dell‟impostazione di un marginRight)

Se si imposta un valore di marginLeft e/o di MarginRight, il valore HorizontalBias=0.5 provvede a centrare l‟oggetto nello spazio rimanente all‟interno dei due margini.

Impostazione da codice

La proprietà HorizontalBias è abbastanza complicata da gestire da codice. Nel caso in cui si preveda di dover riposizionare un oggetto da codice, la soluzione più semplice è quella di impostare sul designer HorizontalBias=0 e gestire la proprietà marginLeft.

ConstraintLayout.LayoutParams params = widget.getLayoutParams();

params.setMarginStart(marginStart); //px

widget.setLayoutParams(params);

params.width = larghezza del widget

params.height = altezza del widget

3. TableLayout

Permette di organizzare i contenuti come se si stesse lavorando con una tabella. L‟organizzazione avviene tramite l‟oggetto TableRow che identifica una riga della tabella All'interno di questo oggetto

dovremo inserire tutti gli elementi che vogliamo mostrare in quella specifica riga. Es di matrice 4 x 4

TableLayout layout = (TableLayout) findViewById(R.id.tableLayout);

for (int i = 0; i < 4; i++) {

TableRow riga = new TableRow(this);

riga.setGravity(Gravity.CENTER);

for (int j = 0; j < 4; j++) {

Button btn = new Button(this);

btn.setId(i * 4 + j);

btn.setOnClickListener(myListener);

riga.addView(btn);

}

layout.addView(riga);

}

Attenzione che eventuali parametri devono essere d tipo TableRow e non di tipo TableLayout.

Page 30: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 30

4. FrameLayout

Il Frame Layout consente la sovrapposizione dei controlli, che vengono posizionati su piani crescenti in base alla posizione del controllo all‟interno del Layout. In pratica mentre il Linear Layout consente di disporre consecutivamente i controlli in verticale o in orizzontale, il Frame Layout consente di impilarli lungo l‟asse z uno per piano. Il metodo bringToFront() consente di spostare in cima l‟elemento corrente Per ogni controllo si può inoltre specificare, tramite layout_gravity, il tipo di ancoraggio

(con i soliti 9 valori: top, bottom, left, right, fill, top|left, top|right, bottom|left, bottom|right).

Accesso ai controlli di un Layout

Associazione di un layout ad una Activity

setContentView(R.layout.activity_main);

Quando si cambia layout associato all‟activity, il layout viene „ricreato‟, per cui non mantiene i valori precedenti. Non solo, ma di conseguenza occorre anche reinizializzare i vari puntatori ai controlli dei

layout ripetendo tutti i vari findViewById.

Navigazione all’interno di un layout

layout.getChildCount(); restituisce il numero di controlli presenti all‟interno del layout

layout.getChildAt(i); restituisce il puntatore all‟oggetto i-esimo figlio del layout (a base 0)

this.getParent(); restituisce il puntatore all‟elemento genitore.

L‟oggetto restituito da .getParent() è un oggetto di tipo ViewParent, per cui occorre eseguire un cast. Per salire ancora di un livello occorre eseguire un cast intermedio al tipo ViewGroup RelativeLayout r = (RelativeLayout) ((ViewGroup) this.getParent()).getParent()

Cilo di scansione dei controlli interni ad un layout

Non sembra possible eseguire direttamente un ciclo for each sui controlli contenuti all‟interno di un layout. L‟indicazione è quella di scorrere l‟elenco dei controlli tramite un ciclo trazionale ed eventualmente aggiunge tutti i controlli all‟interno di un ArrayList scandibile poi tramite for each.

ArrayList<View> myList = new ArrayList<View>();

for (int i = 0; i < wrapper.getChildCount(); i++) {

View child = wrapper.getChildAt(i);

myList.add(v); }

Cancellazione di elementi

layout.removeAllViews(); elimina tutti i controlli contenuti nel layout (statici e dinamici).

layout.removeView(view); elimina uno specifico controllo all‟interno del layout

layout.removeView(layoutInterno); elimina un intero layout con tutti i controlli contenuti

Scroll di un Layout

Per rendere un Linear Layout scrollable è sufficiente racchiuderlo all‟interno di un oggetto ScrollView

<ScrollView android:layout_width="match_parent"

android:layout_height="match_parent" >

<LinearLayout> </LinearLayout>

</ScrollView>

Page 31: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 31

La gestione degli sfondi tramite la classe ColorDrawable

Fino alla versione 21 gli sfondi erano gestiti tramite la classe ColorDrowable che consentiva di leggere /

scrivere i valori tramite I seguenti metodi:

.setBackgroundColor(Color.GREEN); // oppure

.setBackgroundColor(Color.rgb(0, 255, 0)); // oppure

.setBackgroundColor(0xFF00FF00); // Il primo FF è l‟opacity. FF=solido, 00=transparent

.setBackgroundColor(Color.parseColor("#FF00FF00"));

ColorDrawable buttonColor = (ColorDrawable) btn.getBackground();

int colorId = buttonColor.getColor();

dove colorId è una costante che può essere confrontata con Color.RED o Color.rgb(200, 200, 200)

La classe RippleDrawable

Dalla versione 21 in avanti, pur rimanendo la classe precedente, lo sfondo dei pulsanti per default viene impostato di tipo RippleDrawable, una classe particolare che aggiunge intorno al pulsante un bordino trasparente di spessore 5dp, riducendo di fatto le dimensioni per pulsante di 10dp.

In realtà lo spessore del bordo è una percentuale delle dimensioni del pulsante

Se ad un certo punto da codice si va a modificare il colore di sfondo del pulsante mediante il metodo precedente .setBackgroundColor, questo sovrascrive il bordo ingrandendo di conseguenza il

pulsante. Il RippleDrawable è stato pensato per essere applicato staticamente. La sua gestione da codice è piuttosto complicata per cui, se si intende gestire dinamente da codice lo sfondo del pulsante, si può impostare fin dall‟inizio (onCreate) lo sfondo tramite il metodo .setBackgroundColor() che

sostanzialmente sovrascrive la classe RippleDrawable applicando la classe ColorDrawable. In questo modo il bordino esterno di 5dp viene eliminanto fin da subito. La classe GradientDrawable

La classe GradientDrawable consente di applicare come sfondo in gradiente di colore, con la possibilità di impostare i bordi arrotondati ed un bordino esterno che, impostato al valore TRANSPARENT, consente di mantenere un funzionamento simile al RippleDrawable

int[] colors = new int[]{

Color.rgb(214, 215, 215), // grigio originale dei pulsanti Color.WHITE,

Color.rgb(214, 215, 215)

};

GradientDrawable bg = new GradientDrawable();

bg.setGradientType(GradientDrawable.LINEAR_GRADIENT);

bg.setOrientation(GradientDrawable.Orientation.TOP_BOTTOM);

bg.setColor(Color.rgb(214, 215, 215)); // colore singolo

bg.setColors(colors); // vettore di colori

bg.setStroke(10, Color.TRANSPARENT); // spessore (px) e colore del bordo

bg.setCornerRadius(10f); // 10 espresso come float

btn.setBackground(bg);

Page 32: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 32

Immagine di sfondo di un pulsante

btn.setBackgroundResource(R.drawable.myimg);

btn.setBackgroundDrawable(this.getResources().getDrawable(android.R.drawable.

btn_default));

In alternative esiste un comodo pulsante imageButton.

La Gestione dello schermo

Dimensioni dello schermo

Display display = this.getWindowManager().getDefaultDisplay();

DisplayMetrics metrics = new DisplayMetrics();

display.getMetrics(metrics);

screenWidth = metrics.widthPixels;

screenHeight = metrics.heightPixels;

// oppure Point size = new Point();

display.getSize(size);

screenWidth = size.x;

screenHeight = size.y;

Dimensioni dell’Area client di un layout

Window window =this.getWindow();

View clientArea =. Window.findViewById(Window.ID_ANDROID_CONTENT);

int width = clientArea.getWidth();

int height = clientArea.getHeight();

La width dell‟area client coincide con la width del dispositivo. La height non perchè in alto cè la barra del titolo che sottrae spazio

Rotazione dello schermo

if(this.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); else this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

Attenzione che la rotazione del video provoca la terminazione dell‟Activity e successivo riavvio con rigenerazione dell‟evento onCreate. Questo di per sé non è un gravissimo problema e non inficia il buon funzionamento della rotazione, ma il riavvio dell‟applicazione può comunque creare fastidi. Ad esempio se il contenitore imageView all‟avvio caricava staticamente una immagine, questa viene ricaricata ad ogni rotazione.

Per evitare che in corrispondenza della rotazione dell‟immagine venga riavviata l‟Activity, occorre aggiungere la seguente riga:all‟interno del Manifest:

<activity android:name = "MainActivity"

android:configChanges = "orientation|keyboardHidden|screenSize">

Questa riga fa sì che, in caso di variazione della orientation del telefono, oppure in caso di nascondimento della tastiera, oppure in caso di variazione dello sceenSize, l‟applicazione non si riavvii, ma generi semplicemente un evento onConfigurationChanged che può essere semplicemente

gestito nel modo seguente oppure anche non gestito.

public void onConfigurationChanged(Configuration newConfig) {

super.onConfigurationChanged(newConfig); }

Page 33: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 33

INTENT

La classe Intent fornita col framework di Android fornisce i meccanismi necessari per realizzare una

sorta di messaggistica gestita dal sistema operativo con cui un componente può richiedere l’esecuzione di operazioni ad altre componenti.

Tramite gli intent una APP può richiedere al Sistema Operativo una certa azione, come ad esempio il lancio di un‟altra activity o di un Service, oppure per inviare un messaggio di broadcast che può essere ricevuto da qualsiasi app.

Un intent può contenere al suo interno tre informazioni:

action, cioè l‟azione da eseguire. L‟azione da eseguire può essere l‟avvio di una activity, la

visualizzazione di un documento, l‟editazione di un documento, la riproduzione di un filmato o altro ancora. Possibili valori di action sono i seguenti:

ACTION_MAIN Richiesta di Avvio di un componente. Non ha dati in input ACTION_VIEW Richiesta di visualizazione di un insieme di dati indicati tramite URI ACTION_EDIT Richiesta di accesso in editazione a un insieme di dati indicati tramite URI ACTION_DIAL Display the phone dialer with the given number filled in ACTION_ECHO Riproduzione di un file video / audio indicato tramite URI ACTION_REQUEST_ENABLE Richiesta di abilitazione all‟utilizz di uno specifico sensore (GPS)

destinatario del messaggio (component). Può essere un‟Activity, un Servizio o un Broadcast

Receiver. E‟ l‟elemento a cui il SO dovrà inoltrare il messaggio ricevuto.

uri, indica la risorsa da eleborare. Ad esempio una pagina web da visualizzare, un documento

PDF da visualizzare, un numero di telefono da contattare. URI (Uniform Resource Identifier) è una sequenza di caratteri che identifica univocamente una risorsa generica. Sono esempi di URI: un indirizzo web (URL), un documento, un'immagine, un file, un servizio, un indirizzo di posta elettronica simile. URL è strettamente legato alle risorse web, mentre URI è più generico. Si può dire “tutti gli URL sono URI, ma esistono URI che non sono URL. Esempi di URI:

String uri = "http://www.google.com"; (URL)

String uri = "tel:3337684747";

String uri = "sms:3337684747";

String uri = "mailto:[email protected]";

Oltre a quete informazioni un intent può contenere al suo interno dei parametri aggiuntivi (detti Extras) che saranno inoltrati al destinatario insieme al messaggio. Ad esempio nel caso di una mail i vari extras potrebbero essere l‟oggetto, il body, i destinatari per conoscenza, i destinatari per conoscenza nascosta, etc. Intent Espliciti ed Impliciti

Si parla di Intent Esplicito quando è esplicitamente indicato il destinatario del messaggio. Si parla di Intent Implicto quando NON è esplicitamente indicato il destinatario del mesaggio

Per istanziare un Intent esistono ben 7 firme differenti :

1) Intent intent = new Intent()

Istanzia un Intent anonimo. Azione, URI e destinatario verranno specificati in seguito

2) Intent intent = new Intent(Intent i)

Costruttore per copia. Istanzia un Intent copiandolo da un altro Intent passato come parametro.

Page 34: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 34

3) Intent intent = new Intent(String action)

Istanzia un Intent implicito relativo ad una specifica azione, senza indicare però l‟URI

4) Intent intent = new Intent(String action, Uri uri)

Firma tipica per l‟Istanza di un Intent implicito, in cui viene indicata una specifica azione ed uno specifico URI, ma non viene indicato il destinatario

5) Intent intent = new Intent(Context packageContext, Class cls)

Firma tipica per l‟Istanza di un Intent Esplicito in cui si indica esplicitamente il destinatario dell‟intent. In caso di intent esplicito solitamente non serve indicare né l‟action (che sarà quella predefinita del destinatario) né la URI nel caso in cui il destinatario non debba elaborare una risorsa specifica. Con questa firma, il SO non esegue di solito nessuna valutazione sull‟action da eseguire e si limita ad avviare il componente indicato. 6) Intent intent = new Intent(Component component)

Firma analoga alla precedente in cui, però, il destinatario viene identificato come component invece che

come context + class

Il componente destinatrio può essere indicato in due modi:

come Context (package univoco identificativo della app) seguito dal nome della classe da eseguire all‟interno del package (firma 5)

come Component (firma 6)

Partendo da package e classe si può ottenere il component nel modo seguente:

ComponentName component = new ComponentName(this, SecondActivity.class);

7) Intent intent = new Intent(String action, Uri uri, Context packageContext,

Class<?> cls)

Firma completa per l‟istanzia di un Intent esplicito, contenente però anche la action da eseguire e la risorsa su cui operare. Esempio di istanza di un intent implicito

Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));

this.startActivity(intent);

Esempio di istanza di un intent esplicito

Intent intent = new Intent(MainActivity.this, Activity2.class);

this.startActivity(intent);

Utilizzo degli intent impliciti

Nel caso di intent implicito, in che modo il Sistema Operativo riesce ad individuare il destinatrio del messaggio ? Quando si definisce un componente, attraverso il cosiddetto intent-filter è possibile

dichiarare che quel componente è in grado di rispondere a specifici messaggi. L‟ intent-filter può

essere definito staticamente all‟interno del Manifest, oppure anche dinamenicamente all‟interno del codice dell‟Activity, Servizio o Broadcast Receiver.

In corrispondenza di un Intent implicito il SO provvderà ad individuare tutti i receiver compatibili. Nel caso in cui esistano più componenti registrati con lo stesso intent-filter, verrà richiesto all‟utente di decidere quale componente utilizzare per avviare l‟attività. Ad esempio l‟activity principale di un browser avrà, all‟interno del Manifest, un Intent-filter de tipo seguente:

Page 35: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 35

<activity android:name=".BrowserActivitiy">

<intent-filter>

<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />

<data android:scheme="http" />

</intent-filter>

</activity>

Cioè questa activity verrà avviata tutte le volte che verrà richiesta una action di tipo ACTION_VIEW per

una risorsa avente un URI che inizia con i caratteri http:

L’attributo data

Indica il tipo di risorsa che il componente è in grado di servire. Il componente sarà in grado di servire tutte le risorse il cui URI inizia con i caratteri indicati all‟interno dell‟attributo data. L’attributo mimeType

Serve ad identificare meglio (all‟interno dell‟attributo data) il tipo di risorsa che il componente è in grado di servire. Ad esempio un riproduttore audio / video che è in grado di riprodurre qualunque file audio e soltanto i video mpeg potrà avere un attributo mimeType come indicato di seguito.

<intent-filter>

<action android:name=”android.intent.action.ECHO” />

<data android:scheme=“http” android:mimeType=“audio/* />

<data android:scheme=“http” android:mimeType=“video/mpeg />

</intent-filter>

L’attributo category

Fornisce una informazione aggiuntiva rigiardo all‟azione da eseguire. category.LAUNCHER indica che il componente dovrà essere visualizzato nel Launcher come una top-

level application (elenco delle applicazioni disponibili all‟interno dello smartphone) category.DEFAULT indica che il componente deve essere considerato come un componente di

default in grado di soddisfare l‟azione indicata. category.ALTERNATIVE indica che il componente dovrà essere incluso in una lista di componenti

alterntivi comunque in grado di soddisfare l‟azione indicata Se un componente non definisce un apposito intent-filter, potrà essere avviato soltanto tramite un intent esplicito. Metodi per l’invio di un intent al Sistema Operativo

startActivity(intent)

Se si intende inviare il messaggio ad una activity di una app

startService(intent)

Se si intende inviare il messaggio ad un servizio di background in esecuzione sullo smartphone

sendBroadcast(intent)

Se si intende inviare il messaggio in broadcast a tutte i componenti dello smartphone Richiesta di Abilitazione del GPS Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);

startActivity(intent);

Page 36: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 36

Richiesta di Abilitazione del bluetooth Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS); // oppure

Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivity(intent);

Richiamo di una Activity mediante un intent esplicito

Per creare una nuova Activity app / java / tasto destro / New / Activity / Empty Activity. Il nome della nuova activity, come tutte le classi, deve iniziare con una lettere Maiuscola

La nuova activity viene automaticamente inserita nel Manifest

senza però la sezione di <intent-filter> (che, come detto, è facoltativa).

Per ciascuna Activity si può creare un apposito layout di riferimento. In realtà però una Activity1 potrebbe utilizzare più Layout diversi senza necessità di dover istanziare una seconda Activity2. Il fatto di creare due Activity separate, ognuna con il suo layout, consente di suddividere meglio il codice

L’activity principale presenta di default un intent-filter di questo tipo:

<activity android:name=".MainActivity">

<intent-filter>

<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

Il valore ACTION_MAIN indica una activity di tipo MAIN direttamente eseguibile dal sistema operativo.

Il valore category.LAUNCHER indica che l‟activity dovrà essere visualizzata all‟interno del Launcher

con una sua icona. In linea di massima all‟interno di una APP una sola Activity può essere di tipo MAIN / LUNCHER. Nel caso di più Activity di questo tipo, l’Activity da lanciare all’avvio dovrà essere specificata

all’interno della finestra EDIT CONFIGURATION.

Le activity secondarie possono, facoltativamente, essere dichiarate con il seguente intent-filter :

<intent-filter>

<action android:name="android.intent.action.secondActivity"/>

<category android:name="android.intent.category.DEFAULT"/>

</intent-filter>

dove action startSecondActivity indica l‟azione a cui questa activity dovrà rispondere

Intent Esplicto di avvio della seconda activity

Intent intent = new Intent(this, Activity2.class);

this.startActivity(intent);

Intent implicito di avvio della seconda activity

Intent intent = new Intent("android.intent.action.secondActivity");

oppure

Intent intent = new Intent();

intent.setAction("android.intent.action.secondActivity");

this.startActivity(intent)

Page 37: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 37

Ciclo di vita di una Activity

Tutti questi eventi devono, per prima cosa, richiamare l‟evento corrispondente della super-classe, esattamente come avviene per onCreate(). Però, a differenza di onCreate non hanno parametri.

onCreate : evento richiamato al momento del caricamento in memoria

onPause : evento che si verifica quando viene aperta una nuova activity e l‟activity corrente, pur

rimanendo visibile, non è più in esecuzione (equivalente allo stato di ready dei processi) Gli eventi onPause e onResume sono utilizzati per attivare/terminare servizi che devono attivi solo

quando l'activity è in uso. Ad esempio in conseguenza ad una AlertDialog.

onStop : Quando una Activity lancia in esecuzione un‟altra Activity tramite un Intent, automaticamente

passa nello stato di STOP, cioè si trova nello stack delle Activity in esecuzione ma non è al momento visibile. Nel momento in dovesse essere riavviata (mediante REORDER_TO_FRONT oppure se viene di nuovo a trovarsi in primo piano per la chiusura della Activity che si trovava in esecuzione), non viene più generato l‟evento onCreate ma gli eventi onRestart e onStart.

onStop()

Page 38: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 38

In caso di satuazione della memoria, Il Sistema Operativo ha facoltà di „terminare‟ un processo che si trova in stato di STOP. Il processo che viene terminato è quello che si trova in fondo allo stack, cioè quello con utilizzo più lontano nel tempo.

onDestroy : Quest‟evento si verifica quando l‟actvity viene terminata esplicitamente mediante il

metodo finish oppure mediante il tasto BACK dello smartphone.

Il metodo finish() ed il pulsante BACK

il pulsante Back dello smartphone ed il metodo finish() provvedono a terminare l‟activity corrente rimuovendo fisicamente l‟istanza dalla memoria e generando l‟evento onDestroy. Il metodo finish() è un metodo relativo al contesto corrente:

this.finish();

Activity2.this.finish();

Notare che il tasto App recenti dello spartphone apre una schermata con le applicazioni usate recentemente sotto forma di anteprima. In questo elenco compaiono

sia le app in stato di stop (che mantengono lo stato attuale)

sia le app terminate tramite finish o pulsante BACK che, viceversa, non mantengono lo stato. In corrispondenza del loro riavvio verrà rigenerato l‟evento onCreate()

In Lollipop è stata introdotta una grafica a carosello che facilita un rapido passaggio da un'app all'altra

Come riportare una Activity in primo piano

Per riportare una Activity in primo piano è sufficiente settare un apposito flag prima di eseguire startActivity. Con questo flag, se l’activity non è istanziata viene istanziata, altrimenti viene

semplicemente riportata in primo piano.

Intent intent = new Intent(Activity2.this, Activity1.class);

intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

this.startActivity(intent);

Il metodo moveTaskToBack(true)

Consente di portare l‟activity corrente in stato di stop minimizzandola. A seguito della minimizzazione viene visualizzata la seconda app in cima allo stack. Quando l‟Activity ritornerà in primo piano verranno generati gli eventi onRestart() e poi onStart(), ma NON onCreate(). Il parametro True fa sì che la app venga minimizzata anche se il focus non è sulla Activity principale. False invece provoca la minimizzazione solo se il focus è sulla activity principale.

Passaggio di parametri ad una Activity

Prima di richiamare il metodo startActiity(intent), si possono aggiungere all‟interno dell‟intent

eventuali parametri (detti Extras) da passare alla seconda Activity, facendo uso del metodo

intent.putExtra("nomeParametro", valore);

intent.putExtra("parametro1","Ciao");

intent.putExtra("parametro2", (float) 77.77); // default double

intent.putExtra("parametro3", new int[]{23, 12, 54});

intent.putStringArrayListExtra("parametro4", myList);

this.startActivity(intent);

Page 39: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 39

Lettura dei parametri in Activity2

Activity2 può accedere ai parametri ricevuti nel modo seguente:

Intent intent = this.getIntent();

if(intent!=null && intent.hasExtra("parametro1"))

parametro1 = intent.getStringExtra("parametro1");

if(intent!=null && intent.hasExtra("parametro2"))

parametro2 = intent.getFloatExtra("parametro2", 0); (0=defaultValue) parametro3 = intent.getIntArrayExtra("parametro3");

myList = intent.getStringArrayListExtra ("parametro4");

E‟ anche possibile utilizzare il metodo getExtras()che restituisce tutti gli extras all‟interno di un unico

Bundle. Il Bundle (letteralmente fascio) è un oggetto di tipo contentiore che consente di raggruppare

parametri di diversa natura. E‟ lo stesso che viene passano a onCreate() all‟avvio dell‟applicazione, nel qual caso contiene informazioni mirate al mantenimento dello stato.

Bundle bundle = this.getIntent().getExtras();

if (bundle != null && bundle.containsKey(“parametro1”)){

parametro1 = bundle.getString("parametro1");

Passaggio di un oggetto ad Activity2

Tramite il Bundle è anche possibile passare ad Activity2 un intero Object a patto che implementi la classe Serializable

public class Studente implements Serializable {

}

Bundle bundle = new Bundle();

bundle.putSerializable("studente", studente);

bundle.putInt("index", 35);

intent.putExtras(bundle);

In lettura si può utilizzare la seguente sintassi

Bundle bundle = this.getIntent().getExtras();

Studente studente = (Studente) bundle.getSerializable("studente");

int index = bundle.getInt(index)

Proprietà Statiche

Invere di passare un intero oggetto ad Activity2 (ad esempio un intero ArrayList) è possibile dichiarare l‟ArrayList in modo statico all‟interno di Activity1. In tal modo Activity2 potrà accedere all‟oggetto scrivendo semplicemente :

Activity1.myArrayList

Page 40: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 40

Restituzione di un risultato

Se Activity1 intende ricevere una risposta da Activity2, in fase di chiamata non deve utilizzare il metodo startActivity(), ma startActivityForResult() passandogli come secondo parametro un

codice identificativo della richiesta :

final int CODICE_RICHIESTA = 13;

this.startActivityForResult(intent, CODICE_RICHIESTA);

Activity2, una volta letti i parametri ed eseguite le azioni necessarie, potrà presentare all‟utente due pulsanti OK e CANCEL che provvederanno ad inviare all‟utente due risultati differenti.

public void btnOk_Click(View v) {

Intent intent = new Intent();

intent.putExtra("risultato", "parametro aggiuntivo");

this.setResult(RESULT_OK, intent); // RESULT_OK = -1

this.finish();

}

public void btnAnnulla_Click(View v) {

this.setResult(RESULT_CANCELED); // RESULT_CANCELED = 0

this.finish();

}

Notare che setResult() imposta il risulato ma non termina l’Activity corrente che dovrà necessariamente essere chiusa tramite finish (concetto di finestra modale). Su activity1 l‟evento onActivityResult viene generato soltanto in corrispondenza del finish(). Se Activity2 non esegue il finish ma si limita a riportare Activity1 in primo piano tramite il metodo startActivity( ), l‟evento onActivityResult su Activity1 non viene generato. Lettura del risultato da parte di Activity1

Activity1 infine potrà andare a leggere il risultato ricevuto gestendo il seguente evento relativo all‟Activity

onActivityResult(int requestCode, int resultCode, Intent intent)

dove:

Il primo parametro rappresenta il codice relativo alla richiesta inviata da Activity1

Il secondo parametro rappresenta il codice restituito da Activity2

Il terzo parametro rappresenta gli eventuali dati inviati da Activity2 ad Activity1

public void onActivityResult(int reqCode, int resultCode, Intent intent){

if(reqCode == CODICE_RICHIESTA){

String pulsantePremuto = "";

if(resultCode == RESULT_OK)

pulsantePremuto = "RESULT_OK";

else if(resultCode == RESULT_CANCELED)

pulsantePremuto = "RESULT_CANCELED";

String par = null;

if(intent!=null && intent.hasExtra("risultato"))

par = intent.getStringExtra("risultato");

textView.setText(pulsantePremuto + “ – “ + par);

}

}

Page 41: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 41

Menù

Ogni applicazione può possedere un menù attraverso il quale è possibile associare delle azioni di interazione con l‟activity corrente. Ci sono due tipi di menù: OptionsMenu e ContextMenu.

OptionsMenu

E‟ il tipico menù che si apre clickando sui “tre puntini” in alto a destra all‟interno della Action Bar (barra del titolo). Gli OptionsMenù sono associati ad una singola Activity ed espongono le operazioni più importanti che un utente può eseguire relativamente a quella Activity. Da Android 5 in avanti le voci vengono visualizzate in verticale una sotto l‟altra. Le voci scompaiono automaticamente dopo il click su una delle voci oppure dopo un click sull‟area dell‟Activity.

Evento OnCreateOptionMenu

All‟avvio Android crea automaticamente un menù vuoto per l‟Activity corrente. Al termine della fase di avvio richiama automaticamente l‟evento OnCreateOptionMenu (callback

method) a cui viene iniettato come parametro il riferimento al menu. Il programmatore può utilizzare questo evento per popolare il menù.

public boolean onCreateOptionsMenu(Menu menu){

}

Il metodo restituisce un booleano che indica al SO se deve visualizzare oppure no i “tre puntini” di accesso al menù

return true siginifica che il menù è stato popolato e si chiede al SO di visualizzare i tre puntini di apertura del menù medesimo

return false si chiede al SO di NON visualizzare i tre puntini di apertura del menù che potranno essere eventualmente visualizzati in un altro momento.

In genere però, nel caso in cui il menù non sia stato popolato, non si esegue il return false, ma si

richiama il metodo corrispondente della superclasse per dare al sistema l‟opportunità di popolare il menù con eventuali items di sistema.

if (ok) // se il menù è stato popolato

return true;

else

return super.onCreateOptionsMenu(menu);

Aggiunta di nuove voci tramite il metodo add()

menu.add(int groupId, int itemId, int order, CharSequence title)

groupId è un parametro che indica il gruppo di appartenenza della voce menu.NONE = no group

itemId indica l'id da assegnare alla voce del menù

order specifica la posizione della voce all'interno del menù (menu.NONE = ordine naturale).

title specifica la stringa di testo che verrà mostrata all'utente

Esempio: menu.add(Menu.NONE, 1, 1, "Voce 1");

menu.add(Menu.NONE, 2, 2, "Voce 2");

Page 42: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 42

Il gruppo non ha alcuna funzione visuale, ma serve semplicemente a raggruppare le voci da un punto di vista logico in modo da poter applicare al gruppo i metodi di gruppo tipo setGroupCheckable, setGroupEnabled, setGroupVisible. Accesso all’item e relativi metodi

Il metodo .add() restituisce un riferimento all‟item appena aggiunto. Questo riferimento può essere

utilizzato per aggiungere una icona alla voce oppure, ad esempio per renderla selezionabile tramite un checkbox:

MenuItem item = menu.add(Menu.NONE, 3, 3, "voce3"); // oppure

MenuItem item = menu.findItem(R.id.myId); //item id

MenuItem item = menu.getItem(0); //item index

Sul singolo item sono disponibilii seguenti metodi:

item.getItemId() // restitusice l‟ID dell‟item

item.getTitle() // restitusice il testo dell‟item item.setCheckable(true);

item.setChecked(true/false);

item.isChecked() // restitusice true se l‟item è selezionato item.setEnabled(true/false);

item.setVisible(true/false);

item.setIcon(R.drawable.myicon);

Il setCheckable rende la voce selezionabile, ma il check deve essere impostato esplicitamente da

codice in corrispondenza del click tramite setChecked(true) Aggiunta di un Submenu

L‟oggetto Submenu consente di aggiungere al menù corrente un sottomenù a cui possono essere applicati tutti gli stessi identici metodi applicabili al menù principale.

SubMenu menu2 = menu.addSubMenu(Menu.NONE, 6, 6,"Opzioni");

menu2.add(GRUPPO, 11, 1, "Opzione 1");

menu2.add(GRUPPO, 12, 2, "Opzione 2");

menu2.setGroupCheckable(GRUPPO, true, false);

Il metodo .setGroupCheckable consente di rendere selezionabili (con chekbox) tutte le voci del

gruppo. Il secondo parametro se true rende le voci selezionabili.

Il terzo parametro (boolean exclusive), se impostato a true rende le varie voci esclusive,

trasformando di fatto i checkbox in radiobutton (i quadratini dei chekbox diventano cerchi). Evento onOptionsItemSelected di risposta al click sulle voci

Evento generato In corrispondenza del click su una qualunque voce del Menù. Riceve come parametro un riferimento all‟item che lo ha generato. Utilizzabile per impostare / rimuovere manualmente il check

alert(item.getTitle().toString());

alert( Integer.toString(item.getItemId()));

if (item.isCheckable()){

if (item.isChecked())

item.setChecked(false);

else

item.setChecked(true);

}

Page 43: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 43

Risultato restituito da onOptionsItemsSelected

Anche l‟evento onOptionsItemSelected dispone di un return finale.

return true. Significa che l‟evento è stato consumato dal gestore corrente.

return false Significa invece che l‟evento non è stato consumato dal gestore corrente e viene

inoltrato ad altri eventuali gestori che potranno eventualmente gestirlo

Come nel caso precdente, se l‟evento non è stato consumato, al termine si richiama il metodo corrispondente della superclasse e si ritorna il suo risultato.

int id = item.getItemId();

switch (id) {

case 1: ……… break;

case 2: ……… break;

case 3: ……… break;

default: return super.onOptionsItemSelected(item);

}

return true;

Creazione statica di un OptionMenu tramite file xml

Invece di aggiungere dinamicamente le voci, un OptionsMenu può anche essere creato staticamente

aggiungendo all‟interno della cartella delle risorse un nuovo file menu.xml.

Click destro su res / new / Android resource file / tipo=menu

Viene creata, all‟interni di res, una nuova sottocartella menu con all‟interno il nuovo file xml. A differenza del layout, all‟oggetto menu non è associato un designer, per cui occorre lavorare direttamene sul file xml impostando gli stessi attributi dell‟esempio precedente cioè id e title.

<group android:id = "@+id/gruppo1">

<item android:id = "@+id/item1"

android:icon="@drawable/myicon"

android:title="voce1">

</item>

<item android:id = "@+id/item2" android:title="voce2"> </item>

<item android:id = "@+id/item3" android:title="voce3"> </item>

</group>

<!-- check box -->

<item android:id = "@+id/chkBox"

android:title="voce9"

android:checkable="true"

android:order="100">

</item>

<!-- radio button -->

<item android:id = "@+id/opzioni" android:title="opzioni">

<menu>

<group android:checkableBehavior="single">

<item android:id = "@+id/opzione1" android:title="opzione1"></item>

<item android:id = "@+id/opzione2" android:title="opzione2"></item>

</group>

</menu>

</item>

Page 44: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 44

L‟opzione android:checkableBehavior è l‟analogo del metodo .setGroupCheckable e consente

di applicare l‟attributo checkable a tutti gli item di un gruppo senza dover applicare il :checkable ad

ogni singolo elemento. I valori possibili sono: all rende selezionalbili tutti gli elementi del gruppo, visualizzando a fianco di ciascun item un

tipico checkbox single rende selezionalbile un solo elemento alla volta, visualizzando a fianco di ciascun item un

tipico radio button di esclusività none rende gli elementi non selezionabili e a fianco degli item non viene visualizzato nulla.

Impostando il valore android:checkableBehavior=”single” sul gruppo NON bisogna più impostare l‟attributo checkable all‟interno delle opzioni perché altrimenti prevalgono e ritrasformano gli item in check box. Accesso al file di risorsa tramite l’inflater

L'inflating è il meccanismo che permette di istanziare un'oggetto a partire da un file di risorsa.

All‟interno dell‟evento OnCreateOptionMenu occorre „collegare‟ il puntatore menu ricevuto dall‟evento

con il menu creato staticamente all‟interno del file xml. A tal fine si usa l‟oggetto MenuInflater.

public boolean onCreateOptionsMenu (Menu menu){

MenuInflater inflater = this.getMenuInflater();

inflater.inflate(R.menu.menu_statico.xml, menu);

try {

inflater.inflate(R.menu.menu_statico, menu);

return true;

}

catch(Exception ex) {

return super.onCreateOptionsMenu(menu);

}

}

Evento di risposta al click

L‟evento onOptionsItemSelected rimane lo stesso identico utilizzato nel caso del menù dinamico.

ContextMenu

Il ContextMenu è simile al menù associato al tasto destro del mouse su un controllo Windows. Il ContextMenu associato ad un elemento compare in corrispondenza del click prolungato (long click) sull'elemento.

Si possono associare ContextMenu defferenti per ogni controllo. La sintassi è la seguente :

public void onCreate() {

this.registerForContextMenu(lbl);

this.registerForContextMenu(btn);

}

Page 45: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 45

Evento OnCreateOptionMenu

Se un controllo ha associato un ContextMenu, nel momento in cui si esegue un long click sul controllo, viene automaticamente generato l‟evento onCreateContextMenu nel quale è possibile creare dinamicamente il menù da visualizzare. Il menù pertanto può può variare a seconda dei contesti Il problema principale legato all‟evento onCreateContextMenu è che, a differenza dell‟evento OnCreateOptionMenu che viene richiamato soltanto all‟avvio dell‟applicazione, quest’evento viene

richiamato ogni volta che si esegue un long click, per cui il menù viene ogni volta ricreato e lo stato delle varie opzioni deve eventualmente essere salvato all‟interno di una variabile di programma.

pubiic void onCreateContextMenu(

ContextMenu menu,

View v,

ContextMenu.ContextMenuInfo menuInfo) { }

Il 1° parametro è il puntatore al menu a cui appendere le nuove voci Il 2° parametro è un puntatore al controllo che ha scatenato l‟evento Il 3° parametro è un puntatore ad eventuali ulteriori informazioni aggiuntive

// per prima cosa si richiama la superclasse

super.onCreateContextMenu(menu, v, menuInfo);

if(v.getId() == R.id.lblTitolo) {

MenuItem item=menu.add(Menu.NONE, 1, 1, "grassetto");

item.setCheckable(true);

item.setChecked(grassetto);

}

else if(v.getId() == R.id.btn) {

menu.setHeaderTitle("link disponibili:");

menu.add(Menu.NONE, 3, 1,"www.google.com");

menu.add(Menu.NONE, 4, 2,"www.vallauri.edu");

}

dove grassetto è una variabile globale booleana che vale :

true se il grassetto è impostato

false se il grssetto non è impostato

Il metodo activity.openContextMenu(myControl)

Questo metodo, richiamabile da qualunque sezione di codice, consente di aprire da programma il Context Menù dell‟oggetto myControl senza dover eseguire un long click. Evento onContextItemSelected(MenuItem item)

Questo evento viene generato ogni volta che si clicca su una delle voci del menù, esattamente come per gli OptionMenu. Il parametro item rappresenta il puntatore alla voce cliccata.

if(item.getItemId()==1){

grassetto=!grassetto;

if(grassetto) lbl.setTypeface(null,Typeface.BOLD);

}

Dopo il click il menu viene distrutto e, in corripondenza del prossimo long-click, viene rigenerato.

Page 46: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 46

Device File Explorer

Da Android Studio 3.0 in avanti il vecchio Android Device Monitor (DDMS = Dalvik Device Monitor

Service Tools/Android/AndroidDeviceMonitor) non è più integrato in Android Studio, ma è possibile accedere singolarmente ai suoi componenti dal menù View / Tools Windows.

L‟intero ambiente Android Device Monitor rimane comunque disponibile all‟interno dell‟SDK android-sdk/tools/monitor.bat

View / Tool Windows / Device File Explorer è l‟utility che funge da terminale grafico verso l‟emulatore dello smartphone. Consente di accedere al file system dello smatphone, come se avessimo un computer connesso tramite USB allo smartphone.

Le cartelle dati a cui l‟utente ha accesso in scrittura sono sostanzialmente due:

Data (memoria interna)

SdCard (memory card esterna)

In realtà negli smartphone attuali data e sdcard sono due sezioni della stessa memoria

Le apk caricate all‟interno dello smartphone sono posizionate nella cartella data / app

I dati sono invece posizionati all‟interno della cartella data / data / myApp / files /

La cartella files viene creata automaticamente nel momento in cui si istanzia un FileReader oppure un

FileWriter su un file interno a quella cartella. La cartella può anche essere creata manualmente.

Per creare un nuovo file o una nuova cartella all‟interno di una certa cartella è sufficiente fare tasto destro sulla cartella / New. Sempre tramite tasto destro sono disponibili i comandi upload, save as e synchronize che esegue un refresh della cartella. Dentro Data/Data/files / si può ad esempio creare una cartella Music in cui salvare files.mp3.

Nota: In realtà a Run Time eventuali files uploadati all‟interno di Data / Data / files vengono copiati ed utilizzati in una posizione diversa: data / user / 0 / myApp / files /

Per aprire un file un file è sufficiente un doppio click. Attenzione però che Android Studio saves files you open this way in a temporary directory outside of your project. If you make modifications to a file you opened using the Device File Explorer, and would like to save your changes back to the device, you must manually upload the modified version of the file to the device. In pratica occorre gestire il file esternamento e poi fare l‟upload.

Le impostazioni dello smartphone (ad esempio un valore simulato delle coordinate GPS) sono ora configurabili dal menù di comandi sul lato destro dell‟emulatore stesso.

Accesso al File System

Le aree di memoria di una app sono sostanzialmente tre:

Internal Storage (memoria interna) visibile da Android Device Monitor e suddivisa per app. Contiene i dati specifici delle singole app.

External Storage (memoria esterna o SD Memory) che consente di estendere la memoria interna del dispositivo. Essendo rimuovibile, se si effettua il salvataggio di dati sulla SD, è opportuno che i dati salvati non siano indispensabili per il funzionamento dell'applicazione.

Assets / Row Sono aree di memoria interne all‟apk. I dati di queste cartelle sono utilizzabili in sola lettura (essendo interni all‟apk non è consentito modificare la loro dimensione) e NON sono accessibili tramite File System del dispositivo. Sono accessibili da Android Studio in sviluppo.

Page 47: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 47

Accesso ai files memorizzati nel Internal Storage

String path = this.getFilesDir().getPath().toString() + "/filename.txt";

FileReader fileReader = new FileReader(path);

BufferedReader bufferedReader = new BufferedReader(fileReader);

FileWriter fileWriter = new FileWriter(path, true/false)

BufferedWriter bufferedwriter = new BufferedWriter(fileWriter);

// oppure

FileInputStream stream = context.openFileInput("filename.txt");

InputStreamReader streamReader = new InputStreamReader(stream, "UTF-8");

BufferedReader bufferedReader = new BufferedReader(streamReader);

FileOutputStream stream = context.openFileOutput("myText.txt",MODE_PRIVATE);

OutputStreamWriter streamWriter = new OutputStreamWriter(stream);

BufferedWriter bufferedwriter = new BufferedWriter(streamWriter);

Per vedere se un file esiste / creare un nuovo file

File myFile = new File(path);

if(myFile.isFile()) // è un file e non una cartella

FileReader reader = new FileReader(myFile);

else

myFile.createNewFile();

Per leggere il contenuto di una cartella :

File dir = new File(path);

String [] vect = dir.list();

Lettura / Scrittura dei Text File

public void leggi() throws IOException {

try{

FileReader fileReader = new FileReader(path);

BufferedReader br = new BufferedReader(fileReader);

String s, testo="";

// readline elimina il \r\n finale

while((s=br.readLine()) != null)

testo += s + "\r\n";

br.close();

reader.close();

alert(testo);

}

catch (Exception ex) { // pokemon exception ! gotta catch „em all

alert(ex.getMessage());

}

Page 48: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 48

public void salva() throws IOException {

try{

// true = append; false=overwrite (default);

FileWriter fileWriter = new FileWriter(path, true)

BufferedWriter writer = new BufferedWriter(fileWriter);

writer.write("Lorem Ipsum\r\n");

writer.flush();

writer.close();

alert("File Salvato Correttamente");

}

catch (IOException ex) {

alert(ex.getMessage());

}

Accesso ai files memorizzati nell’External Storage

Occorre innanzittutto aggiungere all‟interno del file Manifest (direttamente nel tag principale

<manifest>) la seguente riga:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Il path da indicare per l‟accesso al file sarà il seguente :

String filePath = "/sdcard/filename.txt";

Dopo di che si possono utilizzare gli stessi metodi dell‟Internal Storage

Accesso ai files memorizzati nella cartella Assets

Come detto la cartella Assets viene creata direttamente all‟interno dell‟apk ed i files sono readonly Da Andorid Studio è possibile creare staticamente una cartella Assets aggiungendo al suo interno i files desiderati. A tal fine andare su : app / tasto destro / New / Folder / Assets Folder Come percorso tra Main, Debug, Release va benissimo Main All‟interno della cartella Assets creare il file desiderato.

InputStream stream = context.getAssets().open(“filename.txt”);

InputStreamReader streamReader = new InputStreamReader(stream, "UTF-8");

BufferedReader bufferedReader = new BufferedReader(streamReader);

L’oggetto FileOutputStream

Gli oggetti FileWriter e FileReader sono mirati all‟utilizzo di stream testuali.

FileOutputStream è invece utilizzato nel caso di stream binari che vengono letti byte a byte senza interpretazione dei dati. In questo caso filename non è il path completo, ma il semplice nome del file.

Scrittura

FileOutputStream fs = openFileOutput(nomeFile, Context.MODE_PRIVATE);

fs.write(myVar.getBytes());

fs.close();

Qualsiasi variabile viene salvata su file in forma binaria byte per byte. Questa tecnica potrebbe essere utilizzata per creare i vecchi files dati, ma con estrema difficoltà. Molto più semplice convertire in testo ed usare files testuali gestiti tramite i due oggetti precedenti.

Page 49: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 49

Lettura

File f = getBaseContext().getFileStreamPath(nomeFile);

String ris = "";

if (f.exists()) {

try {

FileInputStream fs = openFileInput(nomeFile);

// Legge tanti bytes quanto è grande tmp

byte[] tmp = new byte[1];

while (fs.read (tmp) != -1)

ris = ris + new String(tmp,"UTF-8");

fs.close();

}

Diritti di accesso

Di default questo tipo di salvataggio è un salvataggio privato. Il file può essere letto e scritto esclusivamente dall'applicazione che lo ha creato e non da altre applicazioni. Per potere accedere al file anche da altre applicazioni su può utilizzare una delle seguenti costanti:

Context.MODE_WORLD_READABLE: viene consentito l'accesso in lettura a tutte le applicazioni

Context.MODE_WORLD_WRITEABLE: viene consentito l'accesso in scrittura a tutte le applicazioni

ListView e Adapter

Le liste visualizzano i record uno sotto l'altro con la possibilità di effettuare il classico scroll.

Principali proprietà:

// come width e height impostare SEMPRE match_parent

android:layout_width:”match_parent”

android:layout_height:”match_parent”

// impostare SEMPRE tutti e 4 i constraint come indicato di seguito

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintTop_toBottomOf="@+id/menuLayout"

app:layout_constraintStart_toStartOf="parent"

app:layout_constraintEnd_toEndOf="parent"

android:choiceMode="singleChoice" // oppure multiple choice

android:fastScrollAlwaysVisible="true" // Barra scorrimento sempre visibile

android:fastScrollEnabled="true"

android:listSelector="blue"

il listSelector definisce il colore di sfondo dell‟elemento selezionato.

Per cambiare il colore del testo dell‟elemento selezionato occorre utilizzare la direttiva android:textColor="@drawable/text_selector"

dove text_selector è un file .xml definito all‟interno della cartella drawable

Se non si specifica il listSelector, non viene usato alcun evidenziatore ma semplicemente, al

momento del click, l‟elemento selezionato si ricolora per un attimo di blu e poi ritora subito grigio.

Page 50: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 50

Scansione degli item di un ListView

listView.getCount(); ritorna il numero di item complesivamente contenuti nel ListView

listView.getChildCount(); ritorna il numero di item attualmente visualizzati for(int i=0 ; i < listView.getCount(); i++) listView.setItemChecked(i, false);

Scroll del List View

Come detto all‟inizio, per avere uno scroll efficace occorre impostare entrambe le dimensioni a match_parent e ancorare il ListView su tutti e 4 i lati

Per eseguire lo scroll da codice : lstCitta.setSelection(index);

La lista verrà visualizzata a partire dall‟elemento avente l‟indice index (ovviamente a base 0). lstCitta.setSelection(2); // La lista viene visualizzata a partire dall‟elemento indicato.

ArrayAdapter

Per il caricamento dei dati da un ArrayList ad un Listiew, occorre utilizzare un Adapter che si occupi di associare il dato corretto in relazione al layout grafico definito per la visualizzzione delle singola righe. Si chiama Adapter in quanto funziona proprio come adattatore tra due elementi: i dati e il layout.

Esistono numerose varianti di questi "adattatori" a seconda della natura dei dati da mostrare. Uno dei più usati è l'ArrayAdapter, che permette di gestire i dati memorizzati sotto forma di array.

Istanza dell’Adapter e associazione con la sorgente dati

ArrayAdapter<String> adapter = new ArrayAdapter<String>(

this,

android.R.layout.simple_list_item_1,

list);

Al costruttore dell'ArrayAdapter occorre passare 3 parametri: 1. Il contesto corrente 2. il nome del layout da utilizzare per la visualizzazione delle singole righe 3. il nome dell'ArrayList che funge da sorgente dati. Invece di un ArrayList, è possibile passare

direttamente un vettore di stringhe. L‟ArrayList però è molto più flessibile.

android.R.layout.simple_list_item_1 è un layout predefinito molto semplice che visualizza su

ogni riga un singolo elemento di testo (ottenuto ad esempio tramite il metodo .toString di un object) android.R.layout.simple_list_item_2 è un layout simile al precedente in cui ogni item viene

suddiviso in due righe, una sotto l‟altra android.R.layout.simple_list_item_checked visualizza a fianco di ogni voce un checkbox

che si attiva in corrispondenza della selezione android.R.layout.simple_list_item_single_choice visualizza a fianco di ogni voce un

radiobutton che si attiva in corrispondenza della selezione android.R.layout.simple_list_item_multiple_choice visualizza a fianco di ogni voce un

checkbox (simile all‟item_checked) che si attiva in corrispondenza della selezione android.R.layout.simple_list_item_activated_1 applica una colore di sfondo all‟item

selezionato.

E‟ inoltre possibile creare layout personalizzati contenenti la rappresentazione di ogni singola riga tramite la combinazione arbitraria di più TextView uno per ogni campo del record.

Page 51: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 51

Note

Tutti questi layout modificano esclusivamente l‟aspetto grafico degli item. Per abilitare / disabilitare la selezione multipla occorre impostare sul ListView : android:choiceMode="multipleChoice" // oppure single choice

L‟utilizzo di list_item_checked o list_item_single_choice che visualizzano graficamente

l‟item selezionato, rendono inutile il listSelector per l‟assegnazione di un colore di sfondo all‟item

L‟oggetto adapter opera soltanto associato ad oggetti come ListView e Spinner ma non ad esempio con un TextView che può contenere un solo elemento.

Collegamento di ListView ad un ArrayList tramite ArrayAdapter

ArrayList<Studente> list;

ArrayAdapter<String> adapter;

ListView lstStudenti;

protected void onCreate() {

list = new ArrayList<Studente>();

String[] vect = new String[] { "Cuneo", "Alba", "Genola", "Bra" };

list.addAll(Arrays.asList(vect));

adapter = new ArrayAdapter<String>(this, simple_list_item_1, list);

lstStudenti = (ListView) findViewById(R.id.lstStudenti);

lstStudenti.setAdapter(adapter);

lstStudenti.setOnItemClickListener(listener);

adapter.notifyDataSetChanged(); // refresh della visualizzazione

Evento ListView.onItemClick

Il metodo di evento onItemClick presenta quattro parametri aventi i seguenti default names:

parent riferimento al controllo che ha scatenato l‟evento (cioè puntatore al ListView)

v Pointer to the item tha was clicked. Riferimento al “personal row_layout” relativo

all’item cliccato. Tramite il metodo v.findViewById(R.id.txtMyTextBox) è

possibile accedere ad un qualsiasi widget interno al row_layout dell‟item cliccato. pos La posizione assoluta dell‟elemento attualmente cliccato.

id The row id of the item that was clicked.

ListView.OnItemClickListener listener = new ListView.OnItemClickListener()

{

public void onItemClick(AdapterView<?> listView, View layout, int pos, long id)

String item = listView.getItemAtPosition(pos).toString();

alert(item);

CheckBox chk = (CheckBox) layout.findViewById(R.id.chkMyCheckBox);

chk.setChecked(!chk.isChecked());

}

};

Il metodo getItemAtPosition restitusice un object, per cui occorre utilizzare .toString().

Page 52: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 52

Evento ListView.onItemLongClick

Il metodo di evento onItemLongClick presenta gli stessi parametri dell‟evento precedente e deve

ritornare un booleano che ha il seguente significato:

return false fa si che che, dopo onItemLongClick venga generato anche onItemClick

return true fa si che che onItemClick non venga generato

Accesso alle voci selezionate in caso di "multipleChoice"

In caso di abilitazione alla selezione multipla, all‟interno dell‟evento onItemClick è possibile

individuare direttamente le voci selezionate utilizzando il seguente codice:

SparseBooleanArray sparseBooleanArray = lstContatti.getCheckedItemPositions();

String s = "Elementi selezionati: " ;

for(int i=0; i < sparseBooleanArray.size(); i++) {

if(sparseBooleanArray.valueAt(i))

s += sparseBooleanArray.keyAt(i) + "; ";

}

alert(s);

Lo SparseBooleanArray serve a mappare numeri interi in booleani in modo molto efficiente.

Spinner

Implementa il tipico Combo Box. Il funzionamento è praticamente identico rispetto al ListView anche se esteticamente meno bello. Occorre definire una sorgente dati di tipo ArrayList ed un apposito adapter. L‟evento non è più setOnItemClickListener ma setOnItemSelectedListener

ArrayList<String> list = new ArrayList<String>();

ArrayAdapter<String> adapter = new ArrayAdapter<String>( this,

android.R.layout.simple_spinner_item, list);

cmbElenco.setAdapter(adapter);

adapter.notifyDataSetChanged();

cmbElenco.setOnItemSelectedListener(new Spinner.OnItemSelectedListener() {

public void onItemSelected(Spinner<?> parent, View v, int pos, long id) {

String item = parent.getItemAtPosition(pos).toString();

alert(item);

}

public void onNothingSelected(AdapterView<?> arg0) { }

});

GridView

Le GridView operano in modo simile rispetto alle ListView. La differenza sta nel fatto, nel caso della GridView, i dati dell‟ArrayList vengono spalmati sulle varie colonne.

Dal layout l‟unica cosa da definire è il numero di colonne: android:numColumns="3"

E‟ anche possibile definire android:numColumns="auto_fit" specificando però columnWidth

final GridView grid = (GridView) findViewById(R.id.grid);

grid.setAdapter(adapter);

Anche la procedura di risposta al click è la stessa identica del ListView. La posizione restituita è una posizione vettoriale che si incrementa partendo da 0 e scorrendo le righe. I vari campi dei dati da visualizzare devono però essere caricati sequenzialmente all‟interno del listView.

Page 53: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 53

Creazione di un Adapter personalizzato

Per la visualizzazione di un elenco di dati, in alternativa ad un Layout costruito dinamicamente da codice, è possibile creare tramite Designer un layout personalizzato in cui si definisce la struttura grafica di ogni singolo record che compone l‟elenco. Per collegare il layout personalizzato alla sorgente dati occorre ancora definire un adapter personalizzato che funge da Binding tra il layout di visualizzazione e la sorgente dati. In questo modo si definisce la struttura grafica del record tramite Designer senza dover far ricorso alla creazione dinamica con codice java. Step 1 Definizione di un rowlayout personalizzato

Come layout base di ogni singolo record si può utilizzare un Linear Layout orizzontale avente: larghezza = match_parent altezza = wrap_content

Al suo interno si posizionano tutti i vari widget relativi al record da visualizzare, ciascuno con un suo ID. Step 2 Definizione di un Adapter personalizzato per la gestione dei dati

L‟Adapter personalizzato può ereditare dalla classe BaseAdapter (Adapter base, occorre però eseguire una implementazione abbastanza complessa) oppure, più semplicemente, può ereditare dalla classe ArrayAdapter<Type> nel qual caso occorre implementare soltanto il metodo

getView(int position, View layout, ViewGroup parent)

Questo metodo viene richiamato automaticamente nel momento in cui, tramite il metodo setAdapter(), si collega un controllo di visualizzazione al Personal Adapter.

In questo momento il Run Time provvede a scorrere una ad una tutte le righe della sorgente dati e, in corrispondenza di ogni riga, provvede a richiamare il metodo getView() dell‟Adapter per aggiungere la

riga all‟interno dell‟Adapter stesso. NOTA: Il controllo linkato deve assolutamente avere la proprietà layout_height="match_parent"

In caso di wrap_content l‟evento viene richiamato più volte per ogni record.

I tre parametri iniettati al metodo getView rappresentano rispettivamente:

la posizione (all‟interno della Sorgente Dati) dell‟elemento che si vuole visualizzare sul layout

un riferimento al layout personalizzato per la visualizzazione della riga (che il primo giro è null)

un riferimento al layout genitore (layout principale)

Il metodo ritorna un riferimento al layout personalizzato in modo che, nei giri successivi, il riferimento possa essere passato come secondo parametro al metodo stesso, evitando di re-istanziarlo ogni volta. public class MyAdapter extends ArrayAdapter<Studente> {

private final Context context;

private final int layout;

private final List<Studente> list;

public MyAdapter(Context context, int layout, List<Studente> list) {

super(context, layout, list);

this.context = context;

this.layout = layout;

this.list = list;

}

Page 54: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 54

public View getView(int position, View layout, ViewGroup parent) {

if(layout==null) {

LayoutInflater inflater = (LayoutInflater)

context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

layout = inflater.inflate(this.layout, parent, false);

}

Contatto contatto = list.get(position);

TextView txtId = (TextView) layout.findViewById(R.id.txtId);

TextView txtName = (TextView) layout.findViewById(R.id.txtName);

txtId.setText(Integer.toString(contatto.getId()));

txtName.setText(contatto.getName());

return layout;

}

Nel momento in cui il chiamante esegue il metodo listView.setAdapter(adapter), l‟adapter

provvede automaticamente a leggere l‟oggetto list (passato al costruttore) e a richiamare n volte il

metodo getView() passandogli i seguenti parametri:

come primo parametro un indice via via crescente che rappresenta l‟indice del contatto da visualizzare

come secondo parametro null se non ci sono layout liberi da poter utilizzare

il terzo parametro è poco chiaro Non vengono però caricati TUTTI i record contenuti all‟interno di list ma, nel momento in cui l‟area visibile del listView risulta “piena”, il meccanismo si interrompe. L'inflating è il meccanismo che permette di istanziare un'oggetto a partire da una risorsa XML.

In pratica :

se il parametro layout è null, tramite l‟inflater viene creato un nuovo oggetto row_layout in cui vengono caricati tutti i dati del contatto corrente.

Al termine l‟oggetto viene restituito al chiamante che provvederà ad appenderlo al ListView.

Finchè il ListView non risulta “pieno”, ad ogni giro viene creato un nuovo row_layout ed appeso al ListView

Nel momento in cui l‟utente eseguirà uno scroll verso il basso, occorrerà caricare un nuovo contatto, però al contempo il primo contatto in alto uscirà dall‟area di visualizzazione, per cui l‟applicazione richiama il metodo getView() passandogli come layout il layout ormai libero in cui era visualizzato il primo item Note

1) Una importante conseguenza di quanto descritto sopra è che all‟interno di un Adapter personalizzato non è pensabile gestire campi di memoria (ad esempio un checkbox) scollegati dalla sorgente dati, che serve appunto a “memorizzare” i dati mentre l‟Adapter funge soltanto da visualizzatore

2) All‟interno degli Adapter personalizzati NON è consentito l‟utilizzo dei Toast

Page 55: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 55

Activity Principale

Dichiarazione e istanza dell‟Adapter hanno la stessa firma vista per l‟Adapter base ma non sono tipizzate

ArrayList<Studente> list;

ListView lstStudenti;

Adapter adapter;

protected void onCreate() {

list = new ArrayList<Studente>();

caricaLista();

adapter = new Adapter(this, R.layout.row_layout, list);

lstStudenti = (ListView) findViewById(R.id.lstStudenti);

lstStudenti.setAdapter(adapter);

lstStudenti.setOnItemClickListener(listener);

Il Listener è lo stesso del ListView base, però questa volta getItemAtPosition(pos) restituisce un

Object (lo studente corrente) invece che una semplice String. pos rappresenta il numero di riga.

lstStudenti.setOnItemClickListener(new AdapterView.OnItemClickListener() {

public void onItemClick(AdapterView<?> parent, View v, int pos, long id)

final Object item = lstStudenti.getItemAtPosition(pos);

Studente studente = (Studente) item;

alert("posiz="+pos + " nome="+studente.getName());

}

Nota sul ClickListener

Attenzione che se il layout dell’adapter personalizzato contiene un check box, il click listener del checkbox sovrascrive il click listener del ListView che non viene più eseguito. Il problema può essere risolto impostando all‟interno dei checkbox del Personal Adapter le seguenti proprietà:

android:focusable="false"

android:focusableInTouchMode="false"

Pulsanti interni all’Adapter

Su un pulsante definito all‟interno di un Adapter è possibile associare in “forma breve” (tramite Designer) un listener definito all‟interno dell‟Activity principale oppure, in alternativa, è anche possibile definire il listener direttamente all‟interno dell‟Adapter.

Page 56: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 56

Shared Preferences

Le SharedPreferences vengono salvate all‟interno di un‟area di memoria comune in cui ogni App può salvare delle variabili in formato chiave/valore, variabili che la App può leggere in qualsiasi momento. Shared nel senso che sono condivise tra tutte le Activity. Ottime per salvare le preferenze dall‟utente. Le SharedPreferences vengono salvate in un file xml in the app data folder, i.e.

/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml

Il metodo getSharedPreferences ritorna la Shared Preference indicata dal primo parametro.

Se non esiste la crea. Il secondo parametro è lo stesso di prima ed indica i diritti di accesso. Si usa sempre MODE_PRIVATE , i valori _READABLE e _WRITEABLE sono stati deprecati.

L‟oggetto Editor consente di modificare le singole variabili di un gruppo di SharedPreferences.

public void btnAggiungiPreferenza(){

SharedPreferences myPrefs =getSharedPreferences("Shared1",MODE_PRIVATE);

SharedPreferences.Editor editor = myPrefs.edit();

editor.putString("nome", "pippo");

editor.putString("cognome", "pluto");

editor.putInt("eta", 18);

editor.remove("cognome");

editor.commit(); // Salva su file

alert("prefernza aggiunta correttamente"); }

public void btnLeggiPreferenza(){

SharedPreferences myPrefs=getSharedPreferences("Shared1",MODE_PRIVATE);

String nome = myPrefs.getString("nome", "Nessun valore");

String cognome = myPrefs.getString("cognome", "Nessun valore");

int eta = myPrefs.getInt("eta", 0);

String s = nome + " - " + cognome + " - " + Integer.toString(eta);

// cognome restituisce “nessun valore”;

alert(s);

}

Gestione dei thread

Utilizzo di un thread generico

Per gestire un thread occorre istanziare due oggetti:

un oggetto Runnable che consente di definire una singola procedura all‟interno di un thread

separato. Runnable è una classe Astratta la cui istanza deve implementare il metodo Run, che è il metodo che consente di definire la procedura da eseguire in un thread separato

un oggetto Handler che è un oggetto contenuto nel namespace osHandler che consente di

controllare il thread definito all‟interno di Runnable (avvio / arresto / terminazione).

La procedura invocata all‟interno di un thread separato può comunque accedere ai controlli dell‟interfaccia grafica.

private Handler threadHandler;

final long INTERVALLO = 1000;

Page 57: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 57

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

threadHandler = new Handler();

}

public void btnAvvia_Click(View v){

// Avvio del thread che verrà avviato fra 1000 ms

// Si aspetta come primo parametro una ISTANZA della classe Runnable

threadHandler.postDelayed(aggiornaGrafica, INTERVALLO);

}

Terminate le istruzioni da eseguire il thread si arresterebbe. Volendo ottenere una esecuzione ciclica, occorre aggiungere un comando di riavvio in modo ricorsivo in coda al thread stesso:

private Runnable aggiornaGrafica = new Runnable() {

@Override

public void run() {

alert("tick"(;

threadHandler.postDelayed(this, INTERVALLO);

}

};

// Arresto del thread

public void btnArresta_Click(View v){

threadHandler.removeCallbacks(aggiornaGrafica);

}

L‟istanza aggiornaGrafica può essere generata anche dentro onCreate, però in tal caso occorre

portare dentro onCreate anche i Listener di ascolto Avvia_Click e Arresta_Click

Sleep di un thread

Esistono due metodi per forzare un thread in stato di sleep:

SystemClock.sleep(100);

Thread.sleep(100);

Nessuno dei però può essere utilizzato all‟interno del thread principale di tipo UI, ma solo ed esclusivamente all‟interno di thread separati. Il secondo interrompe anche il refresh grafico della pagina. Abbastanza pericoloso.

L’oggetto Timer

Molto più complesso del precedente. Anche in questo caso occorre istanziare due oggetti:

un oggetto Timer che consente di eseguire una certa procedura a intervalli regolari

un oggetto TimerTask che contiene la procedura da eseguire

MyTimerTask myTask = new MyTimerTask();

Timer myTimer = new Timer();

// Avvio del timer

myTimer.schedule(myTask, 1500, 3000); // Ritardo del 1° avvio, interval

// Arresto del timer

myTask.cancel();

class MyTimerTask extends TimerTask {

public void run() { txtOutput.setText(Integer.toString(nCounter)); }

}

Page 58: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 58

Basic Activity

La basic activity rappresenta un buon punto di partenza per la realizzazione di applicazioni più moderne. Il layout principale è costituito da un CoordinatorLayout, un Frame Layout potenziato.

Il CoordinatorLayout consente di inserire:

una Toolbar personalizzabile

un FloatingActionButton cioè un pulsante fluttuante posizionato “al di sopra” degli elementi

del layout.

La caratteristica principale del CoordinatorLayout è la possibilità di coordinare l‟animazione e la transizione dei controlli interni, come ad esempio la possibilità di accompagnare un restringimento della Toolbar con corrispondente risalita nell‟interfaccia di una eventuale ListView sottostante. Oppure la possibilità di ancorare il Floating Button ad un altro widget del layout e muoverlo insieme ad esso. Il Floating Button si sposta automaticamente verso l‟alto in corrispondenza della comparsa di uno SnackBar.

Per approfondimenti sul CoordinatorLayout con esempi di utilizzo dei FAB si rimanda al link: https://www.androidauthority.com/using-coordinatorlayout-android-apps-703720/

Material Design: gli oggetti AppBarLayout e Toolbar

Le Activity che ereditano da AppCompatActivity consentono di personalizzare la barra

dell‟applicazione sulle base delle linee guida dettate dal cosiddetto Material Desgin.

L‟oggetto Toolbar è una estensione della vecchia ActionBar (barra del titolo) controllata

esclusivamente dal framework. La Toolbar è disponibile al programmatore sotto forma di widget e deve essere inserita all‟interno di un layout (qualunque layout, anche annidato). Tipicamente viene utilizzata all‟interno di un layout di tipo AppBarLayout

L‟applicazione può „decidere‟ quale Toolbar utilizzare in certo istante utilizzando il seguente metodo :

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

this.setSupportActionBar(toolbar);

Una tool bar può contenere i seguenti elementi:

Navigation Button che può essere una freccia in alto, un menu di navigazione

(DrawerButton), un pulsante di chiusura, di collapse, o altro.

Logo Image che può avere una altezza arbitraria.

Titolo e sottotitolo Se si utilizza un logo, è consigliabile omettere titolo e sottotitolo

Eventuali Custom Views che possono avere una altezza arbitraria.

Action Menu caratterizzato dai tre puntini verticali di destra e contenente il menù settings

Option Menu

All‟interno dell‟applicazione è definito anche un menu_main.xml avente al suo interno una unico item

denominato Settings con id =action_settings

In corrispondenza dell‟avvio l‟evento onCreateOptionsMenu associa il menù al contenuto del file xml.

In corrispondenza del click sulle voci di menù verrà eseguito l‟evento onOptionsItemSelected.

Page 59: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 59

L’oggetto FloatingActionButton (FAB)

Il Floating Action Button (FAB) è un pulsante circolare /sovrapposto ai controlli sottostanti al quale è possibile associare una azione particolarmente importante all‟interno del layout in uso (ad esempio la scrittura di una nuova mail). Come width e height si imposta di solito il valore wrap_content. Come immane da utilizzare come icona richiede escusivamente una immagine .png All‟interno di un layout è possibile inserire contemporaneamente più FloatingActionButtons

Il FloatingActionButton dell‟esempio ha come icona la tipica icona di invio mail (definita all‟interno

della cartella drawable):

app:srcCompat="@android:drawable/ic_dialog_email"

Per modificare icona e colore del Floating Action Button da designer:

android:src="@mipmap/myicon"

app:backgroundTint="@android:color/white"

Per modificare il colore del Floating Action Button da codice:

fab.setBackgroundTintList(ColorStateList.valueOf(Color.BLUE));

fab.setRippleColor(Color.RED); // when pressed

in corrispondenza del click, visualizza uno Snackbar di esempio :

fab.setOnClickListener(new View.OnClickListener() {

public void onClick(View view) { }

}

La proprietà elevation consente di „sollevare‟ verticalmente l‟oggetto con la possibilità di applicare

ombre o altri effetti.

L’oggetto Snakebar

Introdotto in Android 5 è simile al Toast ma, rispetto al Toast, consente di aggiugere un pulsante di feedbak (tipicamente un pulsante di Annulla).

Lo snackBar compare automaticamente sul lato inferiore del layout e scompare sempre automaticamente dopo un certo tempo oppure quando si clicca sul pulsante di feedback.

Il pulsante di feedback viene aggiunto nel momento in cui si assegna un .setAction() allo snakebar

Snakebar senza action

Snackbar.make(view, msg, Snackbar.LENGTH_LONG).show();

Il primo parametro è un puntatore al layout all‟interno del quale far comparire lo snackBar (tipicamente il layout genitore). E‟ possibile passare come parametro un qualunque widget nel qual caso lo snackbar provvederà da solo ad individuare il genitore del widget ricevuto: Il secondo parametro rappresenta il testo da visaulizzare all‟interno dello snackbar Il terzo parametro rappresenta il tempo di visaulizzare dello snackbar public void onClick(View v) {

Snackbar.make(v, "Testo dello sncakbar", Snackbar.LENGTH_LONG).show();

}

Il metodo statico .make() ritorna un puntatore allo snackbar appena creato.

Page 60: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 60

Snakebar con action

// Salvo dentro snack il puntatore allo snackbar

Snackbar snack = Snackbar.make(v, “Messaggio Inviato”, Snackbar.LENGTH_LONG);

// Accedo al contenuto dello snackbar (area interna)

View snackView = snack.getView();

// cambio il colore di sfondo dell’area interna allo snackbar

snackView.setBackgroundColor(Color.GRAY);

// Accedo al testo dello snackbar e lo imposto a giallo

TextView txt = snackView.findViewById(android.support.design.R.id.snackbar_text);

txt.setTextColor(Color.YELLOW); // colore del testo dello snackbar

// Aggiungo un pulsante ed imposto il colore del testo a rosso

// passando null cume 2° parametro viene inserito il pulsante ma non viene associato un listener snack.setAction("Annulla", myListener);

snack.setActionTextColor(Color.RED);

snack.show();

final View.OnClickListener myListener = new View.OnClickListener(){

@Override

public void onClick(View v) {

alert("operazione annullata"); }

};

Navigation Drawer Activity

Il layout principale è costituito da un DrawerLayout (layout a cassetto) che è equivalente ad un

CoordinatorLayout con la possibilità di gestire al suo interno un widget particolare di tipo

NavigationView. All‟interno del DrawerLayout sono presenti:

Un layout di nome app_bar_main.xml che è identico al coordinator layout principale della Basic

Activity.

un widget di tipo NavigationView che punta al layout denominato nav_header_main.xml

Il NavigationView è un menu a comparsa che viene visualizzato in corrispondenza del click sulla

„cassettiera‟ di sinistra (Drawer Button) dell‟Action Bar .

Il menu a comparsa NavigationView è strutturato in due parti:

Una parte superiore di intestazione a sfondo verde strutturata sotto forma di Linear Layout

verticale in cui l‟utente può inserire uno sotto l‟altro i widget desiderati

Una parte inferiore a sfondo bianco contenente il menu vero e proprio, memorizzato all‟interno di un file menu.xml

All‟interno del layout principale, all‟interno del widget NavigationView, le ultime due righe consentono

di specificare :

il layout da utilizzare nella parte superiore di intestazione del NavigationView

il menu da utilizzare nella parte inferiore del NavigationView relativa ai comandi

app:headerLayout="@layout/nav_header_main"

app:menu="@menu/activity_main_drawer"

Page 61: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 61

Il menù activity_main_drawer è a sua volta strutturato in due parti:

Nella prima parte ci sono 4 radio button alternativi rappresentati non con il semplice pallino ma tramite una icona che sovrascrive il pallino. La voce selezionata viene visualizzata a sfondo scuro e rimane “selezionata” anche dopo che viene chiuso il NavigationView. Essendo i pulsanti alternativi (checkableBehavior=”single”), quando si seleziona una nuova voce, la voce

precedente viene automaticamente deselezionata (riprende lo sfondo chiaro).

Nella seconda parte c‟è un sottomenù Communicate costituito da alcuni comandi indipendenti

fra loro che NON mantengono lo stato dopo che sono stati premuti. Codice di Visualizzazione del Drawer Button e apertura del menù

DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);

ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer,

toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);

drawer.addDrawerListener(toggle);

toggle.syncState();

La classe ActionBarDrawerToggle consente di associare un Drawer Layout con una Toolbar

interna ad una Action Bar dell‟Activity corrente, con conseguente visualizzazione del Drawer Button all‟interno della toolbar. Il Navigation Layout verrà automaticamente aperto in corrispondenza del click sul Drawer Button, e automaticamente chiuso in corrsipondenza del click in un‟area dell‟Activity oppure in corrispondenza della seleziona di una delle voci del menu. Presenta i seguenti parametri:

1. Il contest (Activity) corrente 2. Il puntatore al Drawer Layout principale 3. Il puntatore alla Toolbar che si desidera associare con il Drawer Layout 4. Puntatore alla Stringa (costante) da visualizzare in fase di aperture del Drawer Layout 5. Puntatore alla Stringa (costante) da visualizzare in fase di chiusura del Drawer Layout

Il richiamo del metodo .syncState() è obbligatorio e serve a sincronizzare il collegamento (aperture /

chiusura) fra Drawer Layout e Toolbar. Gestione degli eventi

La gestione del click sulle varie voci del menu viene gestita mediante un listener di tipo onNavigationItemSelected che riceve come parametro l‟item su cui è stato eseguito il click :

NavigationView nav = (NavigationView) findViewById(R.id.nav_view);

nav.setNavigationItemSelectedListener(this);

public boolean onNavigationItemSelected(MenuItem item) { }

Al termine della procedura di evento si provvede alla chiusura manuale del menù a comparsa mediante il metodo drawer.closeDrawer().

Evento onBackPressed

Questo evento viene generato in corrispondenza del click sul pulsante BACK dello smartphone. Se il menu a comparsa è aperto viene chiuso, altrimenti viene richiamato il metodo della superclasse.

Page 62: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 62

La gestione dei permessi

Ogni app deve dichiarare all‟inerno del manifest l‟elenco dei permessi di cui ha necessità. Esempi:

<uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.CALL_PHONE"/>

<uses-permission android:name="android.permission.SEND_SMS"/>

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

Se la app richiede permessi “normali” (che non mettono a rischiola privacy dell‟utente o il funzionamento del dispositivo), automaticamente il sistema concede i pemessi richiesti. Nel caso invece in cui I permessi richiesti siano ritenuti rischiosi per la privacy o per il dispositivo, dalla versione 6.0 (API 23) in avanti occorre prevedere una esplicita conferma da parte dell‟utente. Nei dispositivi con SO antecedente alla versione 23, il consenso veniva richiesto in fase di installazione. If the user clicks Accept, all permissions the app requests are granted. If the user denies the permissions request, the system cancels the installation of the app. Nei dispositivi con SO maggiore o uguale alla versione 23, il consenso deve essere esplicitamente richiesto dalla app in fase di runtime. La app deve cioè provvedere a visualizzare una finestra di dialogo di sistema che elenca i permessi necessari e visualizza i pulsanti DENY and ALLOW. Se l‟utente spunta l‟opzione Never ask again, l‟ozione selezionata (DENY oppure ALLOW) l‟opzione viene mantenuta anche nei successive utilizzi. Per richiedere il consenso all‟utente si può utilizzare il seguente codice:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)

== PackageManager.PERMISSION_GRANTED)

lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 10, this);

else

ActivityCompat.requestPermissions(this,

new String[]{ Manifest.permission.ACCESS_FINE_LOCATION},

ACCESS_FINE_LOCATION_REQUEST_CODE);

dove ACCESS_FINE_LOCATION_REQUEST_CODE è una semplice costante utente che assegna un codice

alla richiesta di permesso.

Il metodo requestPermissions(), al termine, genera l‟evento onRequestPermissionsResult a cui

inietta all‟interno del vettore grantResults il contenuto della decisione presa dall‟utente:

public void onRequestPermissionsResult(int requestCode, String permissions[],

int[] grantResults) {

switch (requestCode) {

case ACCESS_FINE_LOCATION_REQUEST_CODE: {

if (grantResults.length > 0 &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

try {

lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 10, this);

}

catch(SecurityException ex ) {

Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();

}

}

else

Toast.makeText(this, "Permessi GPS negati. La app verrà terminata");

return;

}

Page 63: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 63

Accesso ad un web service http

Uno dei metodi più comuni utilizzati dalle applicazioni per comunicare con il mondo esterno è quello di accedere tramite HTTP ai web service dedicati dai quali possono scaricare svariati tipi di contenuti

Affinchè la app possa inviare richieste HTTP all‟esterno occorre registrare i permessi all‟interno del file Manifest, creando una nuova sezione direttamente all‟interno del tag<manifest>

<uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Il primo permesso in realtà contempla anche il secondo L’oggetto Runnable

L‟oggetto Runnable è l‟oggetto base di java per la realizzazione del multi threading.

Una procedura eseguita all‟interno di un oggetto Runnable può comunicare con il thread principale solo attraverso uno scambio di Messages. La classe astratta AsynkTask

L‟oggetto AsynkTask è un wrapper di Android che utilizza un oggetto Runnable per eseguire una certa

procedura in un thread secondario e gestisce la comunicazione fra il thread principale ed il thread secondario senza costringere il programmatore ad entrare nei dettagli di livello più basso (Messages). La classe AsynkTask è stata pensata appositamente come classe di supporto per l‟esecuzione di thread di breve durata che al termine restituisco un risultato al chiamante. Dichiarazione di una classe che eredita da AsyncTask

La classe astratta AsynkTask è una Typed Class (come ad esempio ArrayList) definita però da tre tipizzazioni:

AsyncTask<TParams, TProgress, TResult>

TParams rappresenta il tipo delle variabili che il chiamante può passare al metodo execute (che a sua

volta richiama il metodo doInBackground). Le variabili devono essere tutte dello stesso tipo e

vengono passate attraverso una ellissi. TParams seve appunto a tipizzare questi parametri. Se non si vogliono passare parametri TParams sarà Void.

TProgress rappresenta un object utilizzabile dal metodo onProgressUpdate per aggiornare il

thread principale (UI) sullo stato di avanzamento del thread. Normalmente impostato a null

TResult rappresenta il tipo del risultato restituito dal metodo doInBackground che viene

automaticamente passato all‟evento onPostExecute() Esempio di dichiarazione di una classe InviaRichiesta che eredita da AysncTask:

public class InviaRichiesta extends AsyncTask<String,Void,String> { }

cioè il metodo doInBackground dovrà ricevere come parametro un ellisse (sequenza) di stringhe e

dovrà restituire una stringa che verrà automaticamente passata a onPostExecute.

Page 64: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 64

Metodi della classe AsyncTask

String doInBackground(String... args) unico metodo effettivamente eseguito all’interno

di un thread separato. E‟ di tipo protected, quindi non visibile all’esterno della classe e può essere richiamato tramite il metodo pubblico execute(). E‟ l‟unico metodo astratto che deve obbligatoriamente essere gestito. Gli altri tre metodi sono callback richiamate rispettivamente

in corrispondenza dell‟avvio del thread,

a tempo durante l‟esecuzione del thread,

in corrispondenza della terminazione del thread. I tre puntini indicano che il parametro args è

una ellisse, cioè sequenza di parametri tutti dello stesso tipo che il chiamato tratta come un vettore.

Si aspetta un parametro del tipo indicato da TParams e restituisce un risultato di tipo indicato da TResult che viene passato automaticamente all‟evento onPostExecute

onPreExeute() richiamato nel momento in cui il thread viene avviato. Per eventuali inizializzazioni. protected void onPreExecute() {

progress = ProgressDialog.show(this, "Connecting...", "Please wait!");

}

onProgressUpdate(View v) utilizzato per riportare sulla UI lo stato di avanzamento del thread. Si

aspetta un parametro dei tipo indicato da TProgress che potrebbe essere ad esempio una progress bar di visualizzazione dello stato di avanzamento (sulla base ad es del n. di bytes ricevuti)

onPostExecute(String result) richiamato al termine dell‟esecuzione del thread. Si tratta di una

callback richiamata al termine del metodo doInBackground (cioè in corrispondenza del ricevimento

dei dati dal server).

Il parametro result viene iniettato da AsynkTask. Utilizzato per elaborare il risultato ricevuto dal

server scrivendolo all‟interno di un Object ricevuto dall‟Activity principale.

MainActivity: Istanza della classe ed invo della richiesta

L‟activity principale dovrà istanziare la classe InviaRichiesta e richiamare il metodo execute che

provvede ad attivare il metodo doInBackground della classe AsynkTask.

I parametri possono essere passati indifferentemente al costruttore (che provvederà a salvarli all‟interno di appositi campi privati) oppure direttamente al metodo execute().

InviaRichiesta inviaRichiesta = new InviaRichiesta(url)

inviaRichiesta.execute(“get”, "/cercaStudente", "nomeStudente=pippo");

Per certi versi è preferibile passare i parametri al costruttore perché accetta anche parametri di tipo diverso, mentre execute accetta soltanto ellissi con parametri tutti dello stesso tipo.

Se non si passano parametri a execute, nella dichiarazione della classe InviaRichiesta come primo

parametro TParams si può impostare Void e doInBackground riceverà una ellissi vuota (Void … args).

Se il thread deve effettuare un Toast, occorre passare anche il context.

Se l‟unico metodo utilizzato all‟interno della classe InviaRichiesta è execute() non serve nemmeno salvare il riferimento all‟interno di una variabile

new InviaRichiesta(myParam).execute(“get”, "/cercaStudente",

"nomeStudente=pippo");

Page 65: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 65

Nota sull’indirizzo IP

Se il server è sulla macchina stessa, come indirizzo ip del server occorre utilizzare l‟indirizzo della scheda fisica e NON l‟indirizzo della scheda virtuale 127.0.0.1 e nemmeno localhost che è un alias di 127.0.0.1. Entrambi nel caso dell‟emulatore puntano all‟emulatore stesso che ha un suo indirizzo IP diverso da quello della macchina fisica. Oggetti per l’invio di una richieste HTTP

All‟interno della classe AsyncTask occorre poi utilizzare gli oggetti necesari per gestire l‟invio di una richiesta HTTP al server e la ricezione della relativa risposta. Questi oggetti hanno tutti la caratteristica di esporre metodi sincroni, cioè che non restituiscono il controllo fino a quando non ricevono la risposta dal server, per cui devono essere utilizzati all‟interno di AsyncTask oppure in fase di inizializzazione (splash screen). Soluzione 1 : La classe DefaultHttpClient

public class InviaRichiesta extends AsyncTask <Void, Void, String> {

private Context context;

private String url;

// costruttore

public InviaRichiesta (String url, Context context) {

this.url = url;

this.context = context;

}

protected String doInBackground(String ... args) {

String ris = "";

String metodo = args[0];

String risorsa = args[1];

String parametro = args[2];

url += risorsa;

DefaultHttpClient client = new DefaultHttpClient();

HttpResponse response = null;

try{

if(metodo == "get"){

if(parametro!="")

url +="?" + parametro;

HttpGet request = new HttpGet(url);

response = client.execute(request); // sincrono

}

else if(metodo == "post") {

HttpPost request = new HttpPost(url);

if(parametro!="") {

ArrayList<BasicNameValuePair> listaParametri =

new ArrayList<BasicNameValuePair>();

listaParametri.add(

new BasicNameValuePair("nomeStudente", parametro));

request.setEntity(new UrlEncodedFormEntity(listaParametri));

}

response = client.execute(request); }

Page 66: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 66

// lettura risposta

StatusLine statusLine = response.getStatusLine();

if(statusLine.getStatusCode()==200) {

InputStream is = response.getEntity().getContent();

BufferedReader reader = new BufferedReader(

new InputStreamReader(is, "iso-8859-1"), 8);

ris =reader.readLine();

reader.close();

}

else

ris = “pagina non accessibile”;

}

catch(Exception ex){

toast(ex.getMessage());

return “”;

}

catch (Exception ex)

{

ris = "Exception: " + ex.getMessage();

}

return ris;

}

@Override

protected void onPostExecute(String responseText){

TextView txtRisultato = (TextView) v;

txtRisultato.setText(responseText);

}

URI e URL sono quasi sinonimi. URI è più generico in quanto è applicabile anche alla definizione delle risorse

tramite namespace. L’oggetto HttpGet non ha un metodo setURL ma ha solo un metodo setURI, per cui occorre

passare attraverso setURI. In alternativa si può passare la URL direttamente al costruttore.

Nota Il codice precedente suppone che lo stream di risposta sia costituito da una sola riga. Nel caso in cui lo stream di risposta sia costituito da più righe, si può utilizzare un ciclo while:

StringBuffer sb = new StringBuffer("");

String line="";

InputStream is = response.getEntity().getContent();

BufferedReader reader = new BufferedReader (new InputStreamReader(is));

while ((line = reader.readLine()) != null) sb.append(line);

reader.close();

return sb.toString();

Invece dello StringBuffer si sarebbe potuta usare una semplice String, ma così è più elegante

Soluzione 2 : La classe URLConnection (più semplice)

Questa seconda soluzione ha il pregio di non distinguere sostanzialmente fra richieste GET e POST, entrambe inviate tramite il comando conn.connet();.

In caso di parametri post è sufficiente aggiungerli alla connessione dopo averla aperta.

Il metodo conn.setDoOutput(true) abilita la scrittura di dati sull connessione e trasforma la

chiamata da GET a POST

Page 67: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 67

public class InviaRichiesta extends AsyncTask<Void, Void, String> {

String url;

private ArrayList<Pair> get = new ArrayList<Pair>();

private ArrayList<Pair> post = new ArrayList<Pair>();

public InviaRichiesta(String url) {

this.url=url;

}

protected String doInBackground(Void ... args) {

try{

// lettura dei parametri contenuti nel vettore "get" e concatenamento alla url

for(Pair item : this.get) {

String divisor;

if(url.indexOf('?') == -1)

divisor="?";

else

divisor="&";

url += divisor + item.first + "=" +Uri.encode(item.second.toString())

}

// parsing della url testuale nel corrispondente oggetto

URL _url = new URL(url);

// Apertura connessione TCP

HttpURLConnection conn = (HttpURLConnection) _url.openConnection();

// Aggiunta eventuali parametri POST e impostazione della modalità POST

if (!this.post.isEmpty()) {

// imposta method POST e abilita la scrittura dei par sulla connessione

conn.setDoOutput(true);

String data = "";

// lettura dei parametri contenuti nel vettore "post"

for(Pair item : this.post)

data += item.first + "=" + Uri.encode(item.second.toString()) +"&";

data = data.substring(0, data.length() - 1);

OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());

wr.write(data);

wr.flush();

wr.close();

}

// invio della richiesta

conn.connect();

// lettura della risposta

InputStream in = null;

if (conn.getResponseCode() == HttpURLConnection.HTTP_OK)

in = conn.getInputStream(); // bloccante

else {

in = conn.getErrorStream();

ris = "errore server:\n"+Integer.toString(conn.getResponseCode())+"-";

}

BufferedReader reader = new BufferedReader(new InputStreamReader(in));

ris+=reader.readLine();

reader.close();

in.close();

}

Page 68: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 68

catch (Exception ex) {

ris="ERRORE connessione al sever - " + ex.getMessage();

}

return ris;

}

Note:

Il conn.connect() non è indispensabile. Se non lo si fa, viene fatto in automatico da

getInputStream()

le istruzioni di elaborazione della risposta sono leggermente modificate rispetto alla soluzione

precedente in quanto ora ricevono un oggetto URLConnection e non HttpResponse.

Spostamento del metodo onPostExecute all’interno della classe principale

Sfruttando il fatto che in JAVA al momento dell‟istanza di una classe è possiile definire nuovi metodi della classe stessa oppure redifinire metodi esistenti, il codice del metodo onPostExecute può essere scritto all‟interno della MainActivity al momento dell‟istanza della classe InviaRichiesta.

In questo modo : 1) si evita di dover passare al costruttore i controlli da aggiornare ed il context. L‟unico parametro da

passare al costruttore oppure al metodo execute() sarà la risorsa da richiedere al server 2) Poiché il metodo onPostExecute() deve elaborare lo stream json ricevuto dal server, il suo

contenuto dipende strettamente dal tipo di json ricevuto. Quindi se si dovessero inviare più richieste relative a stream json differenti (elenco, dettagli, etc.), occorrerebbe definire una classe inviaRichiesta per ogni singola richiesta. Oppure definire un‟unica classe e all‟interno del metodo onPostExecute() eseguire uno switch() per identificare la richiesta. Spostando il metodo onPostExecute() dalla classe alla singola istanza, si risolve questo problema. Ogni istanza avrà il proprio metodo onPostExecute() che intercettarà il proprio stream json.

Gestione di uno stream jSon

Il metodo onPostExecure riceve solitamente una stringa JSON che dovrà provvedere a parsificare andando poi a visualizzare le varie informazioni sull‟interfaccia grafica.

Gli oggetti fondamentali per parsificare una stringa JSON sono

JSONArray nel caso in cui la stringa ricevuta faccia riferimento ad un vettore enumerativo. Esempio [ {“nome”:”pippo”, eta:16}, {“nome”:”pluto”, eta:17}, {} ]

JSONObject nel caso in cui la stringa ricevuta faccia riferimento ad un oggetto. Esempio {studenti: [ {“nome”:”pippo”, eta:16}, {“nome”:”pluto”, eta:17}, {} ] }

JSONArray json = new JSONArray(responseText);

JSONObject json = new JSONObject(responseText);

Da un JSONObjet, tramite la chiave, si possono poi estrarre i vari campi utilizzando i seguenti metodi :

.getString(String key) Restituisce il valore stringa corrispondente alla chiave indicata

.getInt(String key) Restituisce il valore intero corrispondente alla chiave indicata

.getBoolean(String key) Restituisce il valore boolean corrispondente alla chiave indicata

.getJSONArray (String key) Restituisce il vettore corrispondente alla chiave indicata

.getJSONObject (String key) Restituisce l‟oggetto json corrispondente alla chiave indicata

Page 69: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 69

Da un JSONArray enumerativo si può estrarre un singolo object nel modo seguente:

JSONObject studente = vect.getJSONObject(i);

Nel caso di un vettore enumerativo valgono tutti i metodi precedenti, con l‟unica differenza che, anziché passare la chiave, occorre passare l‟indice numerico dell‟elemento all‟interno del vettore enumerativo:

String s = vect.getString(i) Integer n = vect.getInt(i) Boolean b = vect.getBoolean(i) JSONObject o = vect.getJSONObject(i) JSONArray v = vect.getJSONArray(i)

Esempio 1

Supponendo che il server invii la seguente stringa:

{"citta" : ["Cune", "Genola", "Bra"]}

Il parsing potrà essere eseguito nel modo seguente:

protected void onPostExecute(String result){

try{

JSONObject json = new JSONObject(responseText);

JSONArray citta = json.getJSONArray("citta");

for (int i = 0; i< citta.length(); i++)

listaCitta.add(citta.getString(i));

adapter.notifyDataSetChanged();

}

Notare adapter.notifyDataSetChanged() per il refresh del ListView,

Gestione del Layout all‟interno dell‟Activity principale

public void onCreate(Bundle savedInstanceState) {

final ListView lstCitta= (ListView) findViewById(R.id.lstCitta);

final TextView txtError = (TextView) findViewById(R.id.txtError);

final ArrayList<String> listaCitta = new ArrayList<String>();

final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,

android.R.layout.simple_list_item_1, listaCitta);

lstCitta.setAdapter(adapter);

InviaRichiesta request = new InviaRichiesta(listCitta, txtErrore, adapter)

request.execute("get", "/elencoCitta");

Esempio 2

Supponendo che il server invii la seguente stringa:

{"studenti" : [\

{"nome":"pippo", "residenza":"fossano", "eta":16}, \

{"nome":"pluto", "residenza":"fossano", "eta":18}, \

{"nome":"minnie", "residenza":"genola", "eta":17} \

]}

Page 70: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 70

Il parsing potrà essere eseguito nel modo seguente:

protected void onPostExecute(String result){

try{

JSONObject jObject = new JSONObject(result);

JSONArray studenti = jObject.getJSONArray("studenti");

for (int i = 0; i< studenti.length(); i++){

JSONObject studente = studenti.getJSONObject(i);

int id = studente.getInt("id");

String nome = studente.getString("nome");

String residenza = studente.getString("residenza");

listStudenti.add (new Studente(id, nome, residenza));

}

adapterStudenti.notifyDataSetChanged();

}

catch (JSONException ex) {

txtErrore.setText("Exception: " + ex.getMessage());

}

}

Richiesta di una immagine al server

public class InviaRichiestaImg extends AsyncTask <Void, Void, Bitmap> {

private String url;

public InviaRichiestaImg (String url) {

this.url = url;

}

@Override

protected Bitmap doInBackground(Void ... args) {

InputStream in=null;

Bitmap bitmap = null;

try {

URL _url = new URL(url);

HttpURLConnection conn = (HttpURLConnection) _url.openConnection();

/* oppure:

URLConnection conn = url.openConnection();

if (!(conn instanceof HttpURLConnection))

throw new IOException("Not an HTTP connection");

HttpURLConnection httpConn = (HttpURLConnection) conn;

*/

conn.setAllowUserInteraction(false);

conn.setInstanceFollowRedirects(true);

conn.setRequestMethod("GET"); // default

conn.connect();

if (conn.getResponseCode() == HttpURLConnection.HTTP_OK){

in = conn.getInputStream();

bitmap = BitmapFactory.decodeStream(in);

in.close();

}

else{

// In caso di NOK in==null : la funzione restituisce un bitmap null

}

}

Page 71: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 71

catch (IOException ex) {

//ex.printStackTrace("Internal Server Error - " + ex.getMessage());

ex.printStackTrace();

}

return bitmap;

}

main

final ImageView img = (ImageView) v.findViewById(R.id.img);

if (path != "") {

InviaRichiestaImg request = new InviaRichiestaImg(url+"/" + path){

@Override

protected void onPostExecute(Bitmap bitmap){

if(bitmap != null)

img.setImageBitmap(bitmap);

}

};

request.execute();

Gestione di uno stream XML

I vari tipi di Nodo

L‟oggetto Node è la struttura più generale da cui ereditano le seguenti classi:

Document: Estende Node per rappresentare (la radice di) un documento. Un Node non può essere

creato se non viene creato un Document Element: Estende Node per rappresentare un elemento

Attr: Estende Node per rappresentare un attributo

Text: Estende Node per rappresentare una sezione #PCDATA (nodo testuale o foglia).

Questi vari tipi di Node sono definiti mediante le seguenti costanti:

1 = Node.ELEMENT_NODE = nodo vero e proprio 2 = Node.ATTRIBUTE_NODE = attibuto 3 = Node.TEXT_NODE = nodo testuale, cioè foglia dell‟albero (valore di un nodo ELEMENT) 8 = Node.COMMENT_NODE = commento 9 = Node.DOCUMENT_NODE = l‟intero xmlDoc subito dopo il parsing

Accesso alla radice dell’albero

Node root = xmlDoc.getDocumentElement(); (Restituisce un Element)

Node root = xmlDoc.getChildNodes().item(0);

Node root = xmlDoc.getElementsByTagName("bookstore")[0];

Node root = xmlDoc.getElementById(String elementId);

Metodi per la navigazione dell’albero

Restituiscono come risultato sempre un generic oggetto di tipo Node di cui occorrerà eventualmente eseguire il cast

Page 72: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 72

.getChildNodes(); collezione (NodeList) dei figli del nodo corrente

.getFirstChild(); primo figlio del nodo corrente

.getLastChild(); ultimo figlio del nodo corrente

.getNextSibling(); prossimo fratello del nodo corrente

.getPreviousSibling(); fratello precedente

.getParentNode(); padre del nodo corrente

.getOwnerDocument() restituisce l‟oggetto Document contenente il nodo

Esempio

Node root = xml.getDocumentElement();

root.normalize();

if (xml.hasChildNodes()) {

Node node = root.getFirstChild();

while (node != null) {

if (node.getNodeType() == Node.ELEMENT_NODE) {

list.add(node.getTextContent());

node = node.getNextSibling();

}

}

// oppure accedo direttamente al vettore dei nomi

// NodeList nodi = doc.getElementsByTagName("nome");

}

Il metodo .normalize() serve ad eliminare eventuali empty node terminali (whitespaces).

Principali Metodi dell’oggetto NodeList (restituita da .getChildNodes())

int getLength() numero dei nodi

Node item(int index) accede all‟elemento iesimo (a base 0)

Altri Metodi dell’oggetto Node

boolean hasChildNodes() verifica la presenza di nodi figli

short getNodeType() restituisce il tipo del nodo (Element ,Text, Attr)

String getNodeName() restituisce il nome di un TAG di tipo Element

setNodeName(String) imposta il nome di un TAG di tipo Element

String getNodeValue() restituisce il contenuto di un nodo di tipo Text (foglia)

setNodeValue(String) imposta il contenuto di un nodo di tipo Text (foglia)

String getTextContent() restituisce il contenuto testuale (foglia) di un Element Node

setTextContent (String) imposta il contenuto testuale di un Element Node

(in pratica consente di associare direttamente una foglia ad un nodo, come InnerText di C#)

Principali Metodi di un nodo di tipo Text

void setData(String data) imposta il contenuto del nodo

String getData() restituisce il contenuto del nodo

Principali Metodi di un nodo di tipo Element

String getTagName() restituisce il nome dell’elemento

NodeList getElementsByTagName(String name) restituisce i sotto-nodi con un certo tag

Page 73: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 73

Principali Metodi per la gestione degli attributi di un oggetto Node

boolean hasAttributes() verifica la presenza di attributi

boolean hasAttribute(String name) verifica la presenza di un attributo

String getAttribute(String name) restituisce il valore di un attributo

void setAttribute(String name, String value) imposta il valore di un attributo

Attr attr getAttributeNode(String name) restituisce un certo attributo come Object

void setAttributeNode(Attr newAttr) aggiunge un nuovo attributo

String attr.getName() restituisce il nome dell‟attributo

String attr.getValue() restituisce il valore dell‟attributo

void attr.setValue(String value) imposta il valore dell‟attributo

Element attr.getOwnerElement() restituisce l‟elemento che contiene l‟attributo

boolean attr.getSpecified() verifica se l‟attributo è stato specificato esplicitamente, oppure

assume il suo valore di default

NamedNodeMap attrList getAttributes() restituisce la lista degli attributi del nodo

int attrList.getLength(); Numero di attributi

Node attrList.item(i) Attributo con indice i (a base 0)

Node attrList.getNamedItem(attributeName);

Node attrList.removeNamedItem(attributeName);

Void attrList.setNamedItem(Node node); aggiunge, o sostituisce se presente, un nodo

all‟insieme Metodi dell’oggetto Document per la creazione di nuovi nodi

Element createElement(String tagName) crea un elemento con un tag

Text createTextNode(String data) crea una sezione #PCDATA (foglia) specificando il contenuto

Attr createAttribute(String name) crea un attributo con un dato nome

Metodi per l’aggiunta / rimozione dei nodi

parentNode.appendChild(Node) Appende in coda un nuovo nodo

parentNode.insertBefore(newNode, childNode) aggiunge newNode davanti a childNode

parentNode.removeChild(childNode) Elimina childNode, lui e tutti gli eventuali nipoti, di

qualunque ordine e grado

Esempio

Element node = xmlDoc.createElement("nodeName");

root.appendChild(node);

node.setTextContent("valore");

// oppure

Text foglia = xmlDoc.createTextNode("salve mondo"); // Nodo di tipo foglia

node.appendChild(foglia);

Esempio di creazione di un nuovo albero

<A id="aa">

<B/>

<C>text</C>

</A>

Page 74: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 74

Document doc = docBuilder.newDocument();

Element elemA = doc.createElement("A");

elemA.setAttribute("id","aa");

doc.appendChild(elemA);

Element elemB = doc.createElement("B");

elemA.appendChild(elemB);

Element elemC = doc.createElement("C");

Text text = doc.createText("text");

elemC.appendChild(text);

elemA.appendChild(elemC);

Parsing di una stringa xml

String xml = ………………………;

DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();

DocumentBuilder docBuilder = builder.newDocumentBuilder();

Document xmlDoc = docBuilder.parse(“myFile.xml”);

Document xmlDoc = docBuilder.parse(new InputSource(new StringReader(xml)));

Il metodo docBuilder.parse() si aspetta come parametro uno dei seguenti oggetti:

un File XML

un oggetto InputStream

un oggetto InputSource che è un stream testuale „specializzato‟ per xml, cioè in grado di

gestire anche i caratteri speciali. Partendo da una Stringa, occorre convertirla in un oggetto

StringReader (che è semplicemente uno stream testuale) e poi in InputSource.

Restituisce il DOM corrispondente al parametro ricevuto. Sintatticamente accetta come parametro una semplice stringa, però questa stringa deve essere un URI, cioè sostanzialmente il path di un documento

Serializzazione di un XML Document

XMLSerializer ser = new XMLSerializer();

ser.setCharOutputStream(new FileWriter("myFile.xml"));

ser.serialize(xmlDoc);

// oppure XMLSerializer ser = new XMLSerializer();

StringWriter sw = new StringWriter();

ser.setCharOutputStream(sw);

ser.serialize(xmlDoc);

String xml = sw.toString();

// oppure StringWriter writer = new StringWriter();

TransformerFactory factory = TransformerFactory.newInstance();

Transformer transformer = factory.newTransformer();

transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");

transformer.setOutputProperty(OutputKeys.INDENT, "yes");

transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");

transformer.setOutputProperty(OutputKeys.METHOD, "xml");

transformer.transform(new DOMSource(xmlDoc), new StreamResult(writer));

String xml=writer.toString();

Page 75: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 75

Accesso a SQLite in locale Esistono diversi modi per creare un database, ma quello più utilizzato è quello di estendere la classe

SQLiteOpenHelper e poi effettuare un overriding del metodo onCreate per poter creare le tabelle che

rappresenteranno la struttura del database. public class SqliteHelper extends SQLiteOpenHelper {

private static Context context = null;

private static final String[] ALL_TABLES = { "studenti", "insegnanti" };

private static final String CREA_TABELLA_STUDENTI = "CREATE TABLE if not

exists studenti (id integer not null primary key, nome TEXT, email TEXT);";

private static final String CREA_TABELLA_INSEGNANTI ="CREATE TABLE if not

exists insegnanti (id integer not null primary key, nome TEXT, email TEXT);";

// costruttore

SqliteHelper(Context context, String dbName) {

// L'ultimo parametro rappresenta la versione iniziale del database

super(context, dbName, null, 1);

this.context = context;

}

public void onCreate(SQLiteDatabase db) {

try {

db.execSQL(CREA_TABELLA_STUDENTI);

db.execSQL(CREA_TABELLA_INSEGNANTI);

showToast("CREATING....");

}

catch (Exception ex) { showToast (ex.getMessage()); }

}

public void onUpgrade(SQLiteDatabase db, int oldVers, int newVers) {

// ricrea tutte le tabelle presenti nel database

for (String s : ALL_TABLES) db.execSQL("DROP TABLE IF EXISTS " + s);

onCreate(db);

}

public class Sqlite {

public static void init(Context context) {

// Se il db non esiste viene creato generando l'eveto onCreate

if (sqliteHelper == null)

sqliteHelper = new SqliteHelper(context, DATABASE_NAME );

}

/* Open database for insert,update,delete in syncronized manner */

private static synchronized SQLiteDatabase open() {

return sqliteHelper.getWritableDatabase();

}

Page 76: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 76

La parola chiave syncronized, utilizzabile anche all‟interno di una classe davanti ad un blocco di

istruzioni, fa si che il blocco di istruzioni racchiuse venga eseguito da un solo thread alla volta. In pratica realizza un specie di semaforo. Nel caso del database fa sì che il db venga aperto in modo sincrono, nel senso che un solo thread alla volta potrà accedere al database. Eventuali Thread successivi verranno accodati al semaforo (un po‟ come il db.serialize() di Node.js). Es:

public void increment() {

synchronized (this) { count++; }

}

Aggiunta di nuovo studente : il metodo db.insert()

public static void aggiungiStudente(Studente studente) {

final SQLiteDatabase db = open();

String name = sqlEscape(studente.getNome());

String email = sqlEscape(studente.getEmail());

ContentValues record = new ContentValues();

record.put(STUDENTE_NOME, name);

record.put(STUDENTE_EMAIL, email);

db.insert(TABELLA_STUDENTI, null, record);

db.close();

}

Query di ricerca : il metodo db.query()

public static Studente ricercaStudente (int id) {

final SQLiteDatabase db = open();

// 1=tableName, 2=arrayOfTableColumns, 3=whereClause,

4=arrayOfWhereArgs, 5=groupBy, 6=having, 7=orderBy, 8=limit;

Cursor cursor = db.query(

TABELLA_STUDENTI, // 1

new String[] {STUDENTE_ID, STUDENTE_NOME, STUDENTE_EMAIL }, // 2

STUDENTE_ID + "=?", // 3

new String[] { String.valueOf(id) }, // 4

null, null, null, null); // 5, 6, 7, 8

Studente studente = null;

if (cursor != null) {

cursor.moveToFirst();

studente = new Studente(cursor.getString(0),

cursor.getString(1), cursor.getString(2));

}

db.close();

return studente;

}

Query di ricerca : il metodo db.rawQuery() // Query grezza

public static List<Studente> ricercaListaStudenti() {

List<Studente> list = new ArrayList<Studente>();

String sql = "SELECT * FROM " + TABELLA_STUDENTI;

final SQLiteDatabase db = open();

Let's suppose you have a table named foo where all columns either allow NULL

values or have defaults.

In some SQL implementations, this would be valid SQL: INSERT INTO foo;

That's not valid in SQLite. You have to have at least one column specified. Il secondo parametro serve a specificare il nome della colonna a cui assegnare NULL per impedire che l’operazione vada in errore.

Page 77: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 77

Cursor cursor = db.rawQuery(sql, null); // 2°= String[] parameters

if (cursor !=null) {

cursor.moveToFirst();

do {

Studente studente = new Studente();

studente.setId(Integer.parseInt(cursor.getString(0)));

studente.setNome(cursor.getString(1));

studente.setEmail(cursor.getString(2));

list.add(studente);

} while (cursor.moveToNext());

}

db.close(); return list;

Unsafe: String whereClause = "column1='" + value + "'"; // SQL injection Safe: String whereClause = "column1=?";

modifica di uno studente avente id indicato : db.update()

public static int aggiornaStudente(Studente studente) {

final SQLiteDatabase db = open();

ContentValues values = new ContentValues();

values.put(STUDENTE_NOME, studente.getNome());

values.put(STUDENTE_EMAIL, studente.getEmail());

int ris = db.update(TABELLA_STUDENTI,

values,

STUDENTE_ID + " = ?",

new String[]{String.valueOf(studente.getId())});

db.close(); return ris;

}

Cancellazione di uno studente avente id indicato : db.delete()

public static void cancellaStudente(int id) {

final SQLiteDatabase db = open();

db.delete(TABELLA_STUDENTI,

STUDENTE_ID + " = ?",

new String[] { String.valueOf(id) });

db.close();

Page 78: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 78

Utilizzo dei sensori

Simulazione delle coordinate GPS

L‟emulatore non può leggere le coordinate GPS, che possono comunque essere impostate :

tramite telnet telnet localhost 5555 (porta di ascolto del dispositivo)

tramite DDMS / Emulator Control geo fix 44.552304, 7.762786

nelle versioni più recenti (android studio 3.0) tramite i comandi a destra dell‟emulatore

Le coordinate GPS vengono azzerate ogni volta che si ricarica il programma nell‟emulatore. Android Manifest

Nell‟android manifest occorre richiedere i diritti per utilizzare il sensore GPS e/o per accedere alla posizione sulla base della cella WiFi a cui si è agganciati.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

ACCESS_COARSE_LOCATION allows an app to access approximate location derived from network

location sources such as cell towers (cella su cui è agganciato il servizio dati) oppure Wi-Fi. Se si intende accedere alla psozione tramite WiFi occorre anche richiedere il permesso ACCESS_WIFI_STATE

Inoltre, per poter accedere alle google maps: <uses-permission android:name="android.permission.INTERNET"/>

Avvio del sensore GPS

L‟Activity principale, oltre ad ereditare da Activity, deve implementare anche l‟interfaccia LocationListener la quale richiede l‟implementazione di quattro metodi astratti (implementabili al

solito con ALT + INVIO) di cui il più importante è onLocationChanged

Prima di andare a leggere la posizione occorre (sull‟onCreate oppure su apposito pulsante) abilitare il sistema di acquisizione: LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);

// getting GPS status

boolean isGPSEnabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);

// getting network status

boolean isNetworkEnabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

// Se il sistema di localizzazione GPS non è abilitato

if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {

// Invio un intent implicito per l'abilitazione del sistema di localizzazione

Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);

startActivity(i);

}

else {

String provider = LocationManager.GPS_PROVIDER;

try {

lm.requestLocationUpdates(provider, 1000, 10, this); // Gestione PERMESSI

abilitato = true;

}

catch(SecurityException ex ) {

Toast.makeText(this, "Errore accesso GPS", Toast.LENGTH_LONG).show();

}

Page 79: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 79

Il metodo lm.isProviderEnabled consente di verificare se è abilitato il GPS oppure se è abilitata la

lettura COARSE relativa alla cella WiFi. Nel caso in cui il GPS non sia attivato, si può richiamare tramite intent implicito un‟apposita Activity di sistema che provvede a richiedere all‟utente se acconsente all‟utilizzo del sistema di localizzazione:

In alternative si potrebbe eseguire una gestione più fine in cui, se non è attivo il GPS ma è attivo il COARSE, si accede alla posizione tramite COARSE nel modo seguente :

Criteria criteria = new Criteria();

// Sets the criteria for a fine and low power provider

criteria.setAccuracy(Criteria.ACCURACY_FINE);

criteria.setPowerRequirement(Criteria.POWER_LOW);

// Scelgo il provider, tra quelli attivi, che meglio soddisfa i criteri impostati

String provider = lm.getBestProvider(criteria, true);

In caso di almeno un provider attivo si può avviare la lettura delle coordinate correnti utilizzando il metodo lm.requestLocationUpdates(provider, 1000, 10, this);

I parametri hanno il seguente significato: 1. Provider Il provider da utilizzare per le registrazioni (COARSE o GPS)

2. minTime intervallo minimo di lettura delle coordinate (in msec)

3. minDistance distanza minima in metri. L‟evento onLocationChanged viene generato ad ogni

minTime in cui la distanza percorsa (rispetto all‟evento precedente) risulta > minDIstance 4. LocationListener è il puntatore all‟istanza di LocationListener all‟interno della quale si trova

l‟evento onLocationChanged che dovrà essere eseguito in corrispondenza del trigger. (this)

L‟oggetto Location corrente viene restituito all‟interno dell‟evento onLocationChanged richiamato

nell‟esempio ogni secondo, ma solo se le coodinate sono cambiate per un valore superiore ai 5 m

public void onLocationChanged(Location loc){

Double lat = loc.getLatitude();

Double lng = loc.getLongitude();

String s = Double.toString(lat) + " - " + Double.toString(lng);

Toast.makeText(this,"s", Toast.LENGTH_LONG).show();

}

Visualizzazione del percorso sulle Google Maps

Per visualizzare una posizione o un percorso sulle google maps si può accedere dirattamente alle ggogle maps passando la posizione oppure punto di partenza e punto di arrivo del percorso:

String addr = "http:/www.google.com/maps/@45,7.7,14z"; // zoom

String addr = "http:/www.google.com/maps?saddr="+coord1+"&daddr="+coord2;

Dopo di che, in entrambii casi, si può utilizzare il seguente codice:

Uri uri = Uri.parse(addr);

Intent intent=new Intent(Intent.ACTION_VIEW, uri);

// intent.setPackage("com.google.android.apps.maps");

// Controllo se il pacchetto per visualizzare mappa è stato caricato

if(intent.resolveActivity(getPackageManager())!=null)

startActivity(intent);

Page 80: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 80

Services and Notification

Un Service è simile ad una Activity ma non ha un layout (demone, in windows service).

Le notification vengono visualizzate sulla barra n alto ogni volta che arriva una notifica Le notifiche sono pending intents. Quando faccio TAP sulla notifica parte la intent relativa.

Solo i Services possono costruire notifiche, le Activity no. Lo scopo è segnalare qualcosa quando non si ha a disposizione il monitor. Il servizio, per segnalare qualcosa all‟utente, l‟unica cosa che può fare è aggiungere qualcosa alla barra delle notifiche.

Se si desidera una sola istanza è consigliabile la classe intentService. Altrimenti è consigliata la classe Service. Creazione di una notifica

Istanziare un Intent per il richiamo dell‟Activity di risposta alla notifica Istanziare un PendingIntent che congloba al suo interno l‟Intent precedente Istanziare una un oggetto Notification che congloba al suo interno l‟oggetto PendintIntent e che mediante il metodo notify() avvisa il NotificationManager del SO public class MyService extends IntentService {

public MyService(){

super ("Mio Servizio"); // La superclasse deve assegnare un nome al servizio

// Nome con cui il sistema riconosce il servizio }

// Evento richiamato in corrispondenza dell'avvio del servizio

protected void onHandleIntent (Intent intent) {

// passo 1 : accesso al NotificationManager // s è la stringa che identifica il Notification Manager

String s = Context.NOTIFICATION_SERVICE;

NotificationManager mng = (NotificationManager)getSystemService(s);

// passo 2 : creo un oggetto Notification che contenga gli estremi della notifica

// da visualizzare sulla barra delle Notifiche int icon = R.drawable.ic_launcher;

CharSequence tickerText = "Breve testo di notifica";

long tempo = System.currentTimeMillis();

Notification notification = new Notification(icon, tickerText, tempo);

// L'oggetto Notification è stato deprecato nelle ultime versioni

// e sostituido da un Builder decisamente più complicato. // NotificationCompat.Builder myBuilder = new NotificationCompat.Builder(this){}

// passo 3 : definisco cosa fare quando l'utente selezionerà (con un tap) la notifica Context context = getApplicationContext();

CharSequence title = "My notification";

CharSequence text = "Testo completo della notifica";

// definisco l'Activity che dovrà essere eseguite sul touch

Intent intent2 = new Intent(this, ActivityNotification.class);

PendingIntent intent1 = PendingIntent.getActivity(this, 0, intent2, 0);

// Associo azione da eseguire in corrispondenza del tap sulla notifica

notification.setLatestEventInfo(context, title, text, intent2);

Page 81: Java per Android - robertomana.altervista.orgrobertomana.altervista.org/wp-content/uploads/2020/03/Java-x-Android.pdf · Tecnologie - Classe Quinta robertomana.it Java per Android

Tecnologie - Classe Quinta robertomana.it

Java per Android

pag 81

// passo 4 : attivo la notifica

String id = this.getString(R.string.HELLO_ID);

mng.notify(Integer.parseInt(id), notification);

}

Avvio del servizio

Il servizio precedente può essere avviato in corrispondenza dell‟arrivo di dati oppure, semplicemente, posizionando un pulsante su una Activity principale :

public void btnClick(View v){

Intent intent = new Intent(MyActivity.this, MyService.class);

startService(intent);

}

Activity di risposta alla notifica

Mostra all‟utente i dati relativi alla notifica e presenta le seguenti istruzioni di accettazione della notifica (eseguite ad esempio tramite un semplice pulsante):

public void btnClick(View v){

String s = Context.NOTIFICATION_SERVICE;

NotificationManager mng = (NotificationManager) getSystemService(s);

String id = this.getString(R.string.HELLO_ID);

mng.cancel(Integer.parseInt(id));

}

Test sulla versione dell’SDK in uso

try {

final PackageInfo info = context.getPackageManager().getPackageInfo(

context.getPackageName(), 0);

targetSdkVersion = info.applicationInfo.targetSdkVersion;

}

catch (PackageManager.NameNotFoundException e) {

e.printStackTrace();

}

// Android M significa Marshmallow (ver 6.0, API 23)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

if (targetSdkVersion >= Build.VERSION_CODES.M) {

}

else {

}

}