Android 2 - Dentro un progetto

Pattern MVC

  • Le applicazioni Android seguono il pattern Model View Controller o MVC
  • Questo pattern prevede che l'APP sia divisa in tre livelli.
  • Il primo livello o Model prevede la descrizione dei dati.
  • Normalmente scriveremo una o più classi, svincolate dall'interfaccia grafica, che incapsulano i dati dell'app e consentono al resto del mondo di accedervi.
  • Un secondo strato, detto View, si occupa della visualizzazione dei dati

Pattern MVC

  • Nel nostro caso questo strato è realizzato tramite i layout, che sono dei file XML che descrivono il contenuto delle varie videate dell'APP
  • L'ultimo strato è il Controller. Esso implementa le azioni dell'utente sul model.
  • Nel nostro caso esso sarà rappresentato da una classe derivata da Activity

Activity

  • Come detto, ogni videata di Android è una Activity
  • Per gestirla ci servirà una classe derivata da Activity o da una sua sottoclasse come ActionBarActivity
  • La classe Activity descive una generica videata. La videata non avrà caratteristiche particolari.
  • In alternativa, ActionBarActivity descrive una videata contenente anche un'Action Bar, vale a dire una o due aree (in alto se una sola, in alto e in basso se due) contente il logo dell'app, il nome ed eventualmente alcune voci di menù, il campo per la ricerca o altro.

Fragment

  • Un fragment è una particolare activity che può essere inserita all'interno di un'altra.
  • L'esigenza dei fragment nasce con l'avvento dei tablet.
  • Le activity vengono progettate per adattarsi allo schermo di un telefono, di dimensione relativamente limitata
  • Con l'avvento dei tablet, dotati di schermi decisamente più grandi, queste activity risultavano decisamente troppo povere.

Fragment

  • Un possibile approccio è quello di Apple, che disegna videate totalemnte separate per iPad e iPhone, ma questo implica uno sforzo di sviluppo notevole e la difficoltà di adattare vecchie app al nuovo dispositivo.
  • L'alternativa di Android è di trasformare le Activity in Fragment, vale a dire in pezzi
  • Avremo poi activity diverse per telefono e tablet, ma in questo caso l'activity per il telefono caricherà il relativo fragment, mentre quella del tablet caricherà contemporaneamente due fragment affiancati, riempiendo lo schermo.

Intent

  • Per passare da un'Activity ad un'altra, sia all'interno di una singola App sia tra app diverse occorre utilizzare un'intent
  • Un Intent sostanzialmente crea un secondo processo con la videata richiesta
  • Ad un'Intent possono essere associati dei parametri, costituiti da coppie nome-valore.
  • Questi parametri devono transitare da un processo ad uno nuovo, quindi possono essere tipi scalari, passati per valore, oppure istanze di classi, ma in questo caso verranno prima serializzate nel chiamante e poi deserializzate nel chiamato.

Intent

  • Gli Intent possono essere espliciti, se richiedono di istanziare una particolare classe, come avviene se richiamo un'activity della mia stessa App
  • Possono anche essere impliciti se indicoano un'attività generica. In questo caso verrà cercata nel sistema un'App contenete un'activity che svolga quell'attività.
  • Se ne sono presenti più d'una (ad esempio ho più browser Web installati), viene presentato un dialog di scelta.

La nostra prima VERA App

Immagine slide
  • Come prima App creeremo un semplice (ed un po' stupido) gioco.
  • Scopo del gioco è indovinare un numero estratto a caso dal nostro telefonino
  • Ad ogni tentativo il telefonino ci indicherà se abbiamo tirato troppo in alto o troppo in basso.
  • Se indoviniamo il numero, ci comparirà una finestra di dialogo con il numero di tentativi che ci sono serviti.
  • La finestra di dialogo i consentirà anche di sciegliere se continuare o terminare l'App.

La nostra prima VERA App

Immagine slide
  • Create quindi un nuovo progetto Android, come abbiamo fatto la volta scorsa, utilizzando come icona l'immagine allegata a questa slide, con il nome AltoBasso

Interfaccia grafica, concetti di base

Immagine slide
  • Come visto nella precedente lezione, l'interfaccia grafica viene disegnata a partire da una serie di elementi precostituiti, utilizzando un comodo editor grafico.
  • Se analizziamo la finestra qui a lato possiamo riconoscere una serie di elementi.
  • La barra in alto, con l'icona ed il nome dell'app è la Action Bar, che dipende dal tipo di classe di base scelta.
  • Sotto la Action Bar vediamo il testo Indovina il numero tra. Esso è contenuto in una TextView

Interfaccia grafica, concetti di base

Immagine slide
  • Troviamo poi il campo d'immissione nel quale scrivere il numero. Questo è un EditText
  • La pagina è chiusa dal bottone che ci consente di provare. È un Button
  • Un componente che non vediamo è quello che serve a disporre gli altri componenti nell'area dello schermo. Nel nostro caso è un LinearLayout verticale

Editor Grafico - Cambio layout

Immagine slide
  • Il layout di default quando si crea l'attività vuota comprende un RelativeLayout che contiene una TextView
  • Dobbiamo trasformarlo nel layout visto nella precedente slide
  • Per fare questo è necessario cambiare il layout di default, che è un RelativeLayout
  • Nel pannello destro, Structure, potremo cliccare con il tasto destro sulla riga RelativeLayout
  • Nel menù contestuale che si apre, selezioneremo Change Layout...

Editro Grafico - Selezione Layout

Immagine slide
  • Nel dialog che si apre cliccheremo sulla tendina New Layout Type:
  • Selezioneremo quindi LinearLayout (Vertical)
  • Confermeremo con Ok

Editor grafico - Aggiunta Campo

Immagine slide
  • È ora il momento di aggiungere il campo nel quale scrivere il numero.
  • Posizioniamoci nella Palette sulla sinistra dell'editor
  • Apriamo la sezione dei Text Fields
  • Scrolliamola fino a che non compare il tipo di campo Number, rappresentato da un campo con il solo numero 42 senza segno
  • Possiamo ora trascinare con il mouse questo campo sotto il testo

Editor Grafico - Aggiunta Bottone

Immagine slide
  • Possiamo ora tornare alla sezione Form Widgets
  • Selezioneremo il Button normale
  • Lo trascineremo sotto il campo

Editor Grafico - Centraggio Bottone

Immagine slide
  • Centreremo ora il bottone sotto il campo
  • Nel pannello Structure sulla destra selezioneremo il bottone Button1 appena inserito
  • Apriremo la sezione Layout Para... cliccando sul + alla sua sinistra
  • Potremo ora andare sul parametro Gravity. Se clicchiamo sul campo del contento ci compare la lista dei possibili valori.
  • Selezioneremo center_horizontal facendo doppio click

Predisposizione delle stringhe

Immagine slide
  • Come abbiamo visto nella precedente lezione, occorre predisporre il file xml con le stringhe che potranno essere poi tradotte.
  • Nel Package Esplorer sulla sinistra apriremo (se già non lo è) il nodo del nostro progetto
  • Possiamo aprire quindi il nodo res ed in questo il nodo values.
  • Dentro a questo nodo troveremo il file strings.xml, che apriremo

Predisposizione delle stringhe

Immagine slide
  • In questo file troviamo già:
    • app_name il nome dell'applicazione
    • action_settings la voce di default del menu
    • hello_world il testo dell'elemento inserito di default, che cancelleremo

Predisposizione delle stringhe

Immagine slide
  • Dovremo aggiungere:
    • messaggio il messaggio che richiede l'inserimento del numero
    • mesaggio_b la seconda parte del messaggio, che separa il minimo ed il massimo
    • bottone il testo del bottone
    • toast_cresci il messaggio per cresci
    • toast_cala il messaggio per cala
    • alert_titolo il titolo del dialog per la vincita
    • alert_messaggio il messaggio, fino al numero di tentativi
    • fine_messaggio la parte finale del messaggio, dopo il numero tentativi
    • alert_nuovo il testo del bottone per richiedere una nuova partita
    • alert_termina il testo del bottone per terminare l'app

Aggiunta di una stringa nell'editor grafico

Immagine slide
  • Per aggiungere una nuova stringa potremo premere il bottone Add...
  • Viene aperta la finestra per la selezione dell'oggetto da aggiungere.
  • Faremo doppio click sulla riga contenete l'oggetto String
  • Ritorneremo nella finestra principale dell'editor
  • Sarà però comparsa una nuova stringa senza nome
  • Nella parte destra dell'editor avremo un campo per il nome ed uno per il valore

Aggiunta di una stringa nell'editor grafico

Immagine slide
  • Compileremo questi campi ed avremo aggiunto la nuova stringa

Aggiunta di tutte le stringhe nell'editor xml

Immagine slide
  • In alternativa all'inserirle ad una ad una nella grafica, è possibile passare alla modalità xml dell'editor

Aggiunta di tutte le stringhe nell'editor xml

Immagine slide
  • Il testo delle stringhe è il seguente:
    <string name="messaggio">Indovina il numero tra </string>
    <string name="messaggio_b">e</string>
    <string name="bottone">Controlla!!!</string>
    <string name="toast_cresci">Cresci</string>
    <string name="toast_cala">Cala</string>
    <string name="alert_titolo">Hai vinto</string>
    <string name="alert_messaggio">Hai indovinato il numero in </string>
    <string name="alert_fine_messaggio"> tentativi</string>
    <string name="alert_nuovo">Ricomincia</string>
    <string name="alert_termina">Termina</string>

Modifica dell'interfaccia nell'editor XML

Immagine slide
  • Ora non rimane che modificare l'identificatore degli elementi grafici e le stringhe da visualizzare.
  • Piuttosto che modificarle nella grafica, questa volta le cambieremo nell'editor XML, utilizzando l'autocompletamento per individuare le stringhe.
  • Toglieremo anche le righe relative al bordo intorno ai componenti, che non vogliamo.

Modifica dell'interfaccia nell'editor XML

Immagine slide
  • Il testo xml della finestra risulta quindi il seguente:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="it.docsalvi.altobasso.MainActivity" >
    <TextView
    android:id="@+id/prompt"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/messaggio"
    android:textAppearance="?android:attr/textAppearanceLarge" />
    <EditText
    android:id="@+id/numero"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ems="10"
    android:inputType="number" >
    <requestFocus />
    </EditText>
    <Button
    android:id="@+id/bottone"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="@string/bottone" />
    </LinearLayout>

Codice - Predisponiamo alcune variabili e costanti

Immagine slide
  • È giunto il momento di cominciare a scrivere codice.
  • Andiamo quindi alla nostra classe MainActivity.java, che dovrebbe essere già aperta.
  • Prima di entrare nel vivo del progetto, predisponiamo alcune variabili e costanti che ci serviranno in seguito:
    static final int MINVALUE = 5;
    static final int MAXVALUE = 15;
    static int numProve;
    static int daIndovinare;
  • Le due costanti sono i limiti del numero da indovinare.

Codice - Predisponiamo alcune variabili e costanti

Immagine slide
  • Abbiamo poi numProve che è il contatore delle prove
  • E per finire daIndovinare che è il numero casuale estratto.

Codice - La parte di default

Immagine slide
  • La maggior parte del nostro lavoro si svolgerà nel metodo onCreate, che esegue l' Override della classe base.
  • Questo metodo è responsabile dell'aspetto iniziale della videata.
  • Lo troviamo già predisposto dal wizard nella sua versione minimale.
  • La prima riga che troviamo in questo metodo è:
    super.onCreate(savedInstanceState);

Codice - La parte di default

Immagine slide
  • Ovviamente questo richiama il metodo corrispondente della superclasse, per consentirle di inizializzare la sua parte di videata.
  • La seconda ed ultima riga che troviamo è:
    setContentView(R.layout.activity_main);
  • Questa parte costruisce la videata a partire dal nostro layout.

Codice - Accesso alla TextView e recupero stringhe

Immagine slide
  • A questo punto la videata contiene tutti gli elementi che abbiamo richiesto, ma dobbiamo potervi accedere da codice per gestirli.
  • Per recuperare la TextView, il cui ID è prompt, useremo la seguente riga di codice:
    TextView prompt = (TextView)findViewById(R.id.prompt);
  • Il metodo findViewById richiede l'ID che è un identificatore numerico univoco. Ricerca l'elemento tra i componenti della videata correte e lo restituisce.
  • Il findViewBiId ritorna comunque una View che è la classe base di ogni componente

Codice - Accesso alla TextView e recupero stringhe

Immagine slide
  • Dovremo quindi fare un type cast al componente che ci serve, prima di assegnare il valore alla variabile
  • Componiamo ora la stringa del prompt. Essa sarà Indovina il numero tra MINVALUE e MAXVALUE dove MINVALUE e MAXVALUE sono costanti ed il testo deve poter venire trdotto.
  • Per recuperare il testo delle due stringhe potremo usare il metodo getResources().getString(<ID>); dove <ID> sarà R.string.messaggio per la prima parte e R.string.messaggio_b per la seconda
  • Per convertire in stringa le due costanti utilizzeremo come al solito String.valueOf(<valore>);

Codice - Accesso alla TextView e recupero stringhe

Immagine slide
  • Una volta costruita la nostra stringa potremo metterla nella nostra TextViev con il metodo prompt.setText(p);

Codice - Associazione di azioni ai bottoni e onClickListener

Immagine slide
  • Dobbiamo ora associare un'azione al bottone.
  • Per prima cosa recuperiamo il bottone con il solito metodo:
    Button b = (Button)findViewById(R.id.bottone);
  • Aggiungiamo ora il listener per l'evento OnClick al bottone, come veniva normalmente fatto per i bottoni di AWT o Swing.
  • Il metodo b.setOnClickListener(<OnClickListener>); richiede come parametro un'istanza di una classe che implementi l'interfaccia OnClickListener.

Codice - Associazione di azioni ai bottoni e onClickListener

Immagine slide
  • Usualmente noi aggiungeremmo alla nostra classe MainActivity implements OnClickListener, quindi, nell'unico metodo onClick(View v) dovremmo inserire tutte le azioni, individuando i vari bottoni con una cascata di if.
  • Per evitare questo, si utilizza una caratteristica delle nuove versioni di Java. Al posto di un'istanza di classe esistente, ne creiamo una all'interno della chiamata:
    b.setOnClickListener(new OnClickListener() {
    ...
    });

  • Questo metodo crea un'istanza di una classe anonima che implementa OnClickListener.

Codice - Associazione di azioni ai bottoni e onClickListener

Immagine slide
  • Tra le graffe dovremo scrivere l'implementazione della nuova classe, che nel nostro caso vorrà dire scrivere un metodo onClick che si implementi l'interfaccia.

Codice - Il metodo onClick ed I Toast

Immagine slide
  • Come la volta scorsa dovremo recuperare dall'interfaccia il campo:
    EditText t = (EditText)findViewById(R.id.numero);
  • Recuperiamo quindi il testo:
    String tent = t.getText().toString();
  • Dobbiamo convertire in numero il dato scritto dall'utente. Il metodo di conversione può generare un'eccezione, quindi metteremo il tutto in un blocco try-catch, ma la clausola catch non farà niente:
    try {
    int tentativo = Integer.parseInt(tent);
    ...
    } catch (Exception e) {
    }

Codice - Il metodo onClick ed I Toast

Immagine slide
  • Se il numero scelto corrisponde a quello estratto, chiameremo un metodo vincita di MainActivity, che descriveremo in seguito:
    if (tentativo == daIndovinare) {
    vincita ();
  • Nel caso invece il numero non sia stato indovinato, indicheremo all'utente se crescere o calare il suo tentativo.
  • Invece di mostrare questo suggerimento con una finestra di dialogo o con un elemento poco visibile nell'interfaccia, utilizzeremo un Toast (che corrisponde ad un colpetto sulla spalla).

Codice - Il metodo onClick ed I Toast

Immagine slide
  • Il codice è il seguente:
    Toast.makeText(getApplicationContext(),getResources().getString(<ID messaggio>), Toast.LENGTH_SHORT).show();
  • Il metodo Toast.makeText restituisce un Toast costruito a partire da un testo
  • Il metodo show() di toast, alla fine, visualizza il Toast creato
  • Il metodo getApplicationContext() di Activity ritorna il contesto dell'App, necessario per creare un Toast
  • Come al solito getResources().getString(<ID messaggio>) recupera il testo del mesaggio, dato il suo ID.

Codice - Il metodo onClick ed I Toast

Immagine slide
  • Per finire, la costante Toast.LENGTH_SHORT indica che il Toast sarà mostrato per un breve periodo.
  • Chiudiamo la funzione con :
    numProve ++;
    t.setText("");
    Per incrementare il numero di prove e svuotare il campo.

Codice - Appoggio - La funzione resetGicoco

Immagine slide
  • Alla fine della onCreate di MainActivity dobbiamo inizializzare le variabili.
  • Dato che la stessa cosa dovrà essere fatta nella funzione della vincita, per cominciare un nuovo gioco, questa inizializzazione la faremo in un metodo separato.
  • Creiamo quindi questo metodo:
    void resetGioco () {
    Random r = new Random();
    numProve = 1;
    daIndovinare = r.nextInt(MAXVALUE - MINVALUE) + MINVALUE;
    }
  • Non credo siano necessari commenti...

Codice - La funzione della vincita ed i dialog

Immagine slide
  • Per semplificare la lettura del codice, la vincita è gestita da un metodo separato.
  • In caso di vincita verrà presentata una finestra di dialogo con il numero di prove e due bottoni: uno per fare un nuovo gioco ed uno per terminare.
  • Per creare il dialog, utilizzeremo il metodo statico della classe AlertDialog:
    AlertDialog alertDialog = new AlertDialog.Builder(this).create();
  • Imposteremo poi il titolo, recuperandolo dalle strings:
    alertDialog.setTitle(getResources().getString(R.string.alert_titolo));

Codice - La funzione della vincita ed i dialog

Immagine slide
  • Costruiremo il testo del messaggio, inserendovi il numero di tentativi e lo inseriremo nel dialog:
    alertDialog.setMessage(getResources().getString(R.string.alert_messaggio) + " " + string.valueOf(numProve) + " " + getResources().getString(R.string.alert_fine_messaggio));

Codice - La funzione della vincita ed i dialog

Immagine slide
  • Imposteremo poi il primo bottone (DialogInterface.BUTTON_POSITIVE) assegnandogli il nome e l'azione:
    alertDialog.setButton( DialogInterface.BUTTON_POSITIVE, getResources().getString(R.string.alert_nuovo), new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
    resetGioco ();
    }
    });

Codice - La funzione della vincita ed i dialog

Immagine slide
  • Faremo la stessa cosa per il secondo bottone (DialogInterface.BUTTON_NEGATIVE):
    alertDialog.setButton( DialogInterface.BUTTON_NEGATIVE, getResources().getString(R.string.alert_termina), new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
    finish();
    }
    });
  • E per finire visualizzeremo la finestra di dialogo:
    alertDialog.show();

Download del codice

[any material that should appear in print but not on the slide]