Corso programmazione Android Lezione 6: Database e Intent

corso programmazione android lezione 6

Corso programmazione Android Lezione 6: Database e Intent

Le altre lezioni del Corso di Programmazione Android sono reperibili a questo indirizzo

In questa lezione del corso impareremo a gestire la persistenza dei dati grazie ai database e a gestire gli Intent utilizzati ,nel caso specifico, per l’apertura di una nuova Activity.
Il codice sorgente mostrato in questa lezione è reperibile qui.

Teoria

Persistenza dei dati su Android

Anche se abbiamo a che fare con device con spazio su disco limitato, spesso è necessario archviare una piccola quantità di dati sul dispositivo, sia per velocizzare la navigazione che come nel nostro caso per archiviare semplicemente dei dati.
Il modo più efficiente Android è l’utilizzo dei database. L’architettura di Android metta a disposizione un database relazionale leggero, chiamato SQLite.

Definizione della tabella BEREADER_TABLE

La nostra applicazione per adesso ha bisogno di una sola tabella che chiameremo BEREADER_TABLE.
La nostra tabella sarà composta dai seguenti campi:

  • _id: la chiave primaria, verrà incrementata in automatico ad ogni inserimento;
  • Tipologia: gestibile tramite uno Spinner potrà contenere “Libri”, “Dischi” e “Fumetti”;
  • Titolo;
  • Autore;
  • Formato: sempre gestibile tramite uno Spinner, potrà contenere “Cartaceo” e “Digitale”;
  • Genere;
  • Posizione: la posizione fisica del nostro oggetto, potrebbe essere libreria, disco esterno, portatile, nonna, suocera, casa di mamma, ecc. Può aprirci la strada della Query personalizzate, ad esempio quali libri ho da quella s….. simpatica signora di mia suocera?
  • Prestato: può contenere si o no
  • Prestato_chi: a chi l’ho prestato? Anche questo può essere utile per le Query personalizzate
  • Prestato_quando: Quando l’ho prestato? Ci servirà per gli avvisi in Homepage. “Attenzione hai prestato a quella s…. simpatica signora di tua suocera il libro …… 3 anni fa. Contattare un killer” (ovviamente il libro è solo un pretesto)

Uno spinner non è altro che un componente dell’interfaccia Android che permette di scegliere gli elementi tramite un menu a tendina. Il suo aspetto è simile a questo:

corso programmazione android lezione 6

Pratica

La classe DatabaseBeReader

Per la gestione dei Database dobbiamo creare una classe apposita, la riporto interamente:

package com.begeekmyfriend.bereader;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
//import android.widget.Toast;

/**
 * Created by fabrizio on 15/03/16.
 */

public class DatabaseBeReader
{
    SQLiteDatabase mDb;
    DbHelper mDbHelper;
    Context mContext;
    private static final String DB_NAME="bereader_db";
    private static final int DB_VERSION=1;

    public DatabaseBeReader(Context ctx)
    {
        mContext=ctx;
        mDbHelper = new DbHelper(ctx, DB_NAME, null, DB_VERSION);
    }

    static class BeReaderMetaData
    {
        static final String BEREADER_TABLE = "bereader_table";
        static final String ID = "_id";
        static final String TIPOLOGIA = "tipologia";
        static final String FORMATO = "formato";
        static final String TITOLO = "titolo";
        static final String AUTORE = "autrre";
        static final String GENERE = "genere";
        static final String POSIZIONE = "posizione";
        static final String PRESTATO = "prestato";
        static final String PRESTATO_CHI = "prestato_chi";
        static final String PRESTATO_QUANDO = "prestato_quando";
    }

    private static final String BEREADER_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS "
        + BeReaderMetaData.BEREADER_TABLE + " ("
        + BeReaderMetaData.ID+ " integer primary key autoincrement, "
        + BeReaderMetaData.TIPOLOGIA+  " text not null, "
        + BeReaderMetaData.FORMATO+  " text not null, "
        + BeReaderMetaData.TITOLO+ " text not null, "
        + BeReaderMetaData.AUTORE+ " text,"
        + BeReaderMetaData.GENERE+ " text,"
        + BeReaderMetaData.POSIZIONE+ " text,"
        + BeReaderMetaData.PRESTATO+ " text not null,"
        + BeReaderMetaData.PRESTATO_CHI+ " text,"
        + BeReaderMetaData.PRESTATO_QUANDO + " text"
        +");";


    public void open()
    {
        mDb = mDbHelper.getWritableDatabase();
    }

    public void close()
    {
        mDb.close();
    }

    public void inserisci(String tipologia, String formato, String titolo,String autore, String genere, String posizione,String prestato, String prestato_chi, String prestato_quando)
    {
        ContentValues cv = new ContentValues();
        cv.put(BeReaderMetaData.TIPOLOGIA, tipologia);
        cv.put(BeReaderMetaData.FORMATO, formato);
        cv.put(BeReaderMetaData.TITOLO, titolo);
        cv.put(BeReaderMetaData.AUTORE, autore);
        cv.put(BeReaderMetaData.GENERE, genere);
        cv.put(BeReaderMetaData.POSIZIONE, posizione);
        cv.put(BeReaderMetaData.PRESTATO, prestato);
        cv.put(BeReaderMetaData.PRESTATO_CHI, prestato_chi);
        cv.put(BeReaderMetaData.PRESTATO_QUANDO, prestato_quando);
        mDb.insert(BeReaderMetaData.BEREADER_TABLE, null, cv);
    }

    public Cursor fetch()
  {
    return mDb.query(BeReaderMetaData.BEREADER_TABLE, null, null, null, null, null, null);
  }

    public Cursor fetchByTipologia(String tipologia)
    {
        return mDb.query(BeReaderMetaData.BEREADER_TABLE, null, BeReaderMetaData.TIPOLOGIA+"='"+tipologia+"'", null, null, null, null);
    }

    public Cursor fetchById(String _id)
    {
        return mDb.query(BeReaderMetaData.BEREADER_TABLE, null, BeReaderMetaData.ID+"='"+_id+"'", null, null, null, null);
    }

    public void editById(String id, String campo, String nuovo_valore)
    {
        String editQuery = "UPDATE "+ BeReaderMetaData.BEREADER_TABLE+" SET "+campo+" = '"+nuovo_valore+"' WHERE "+ BeReaderMetaData.ID+"= '"+id+"';";
        mDb.execSQL(editQuery);
    }

    public void cancellaById(String id)
    {
        String deleteQuery = "DELETE FROM "+BeReaderMetaData.BEREADER_TABLE+" WHERE "+BeReaderMetaData.ID+"='" + id + "';";
        mDb.execSQL(deleteQuery);
    }

    private class DbHelper extends SQLiteOpenHelper
    {
        public DbHelper (Context context, String name, CursorFactory factory, int version)
        {
            super(context, name, factory, version);
        }

        @Override
        public void onCreate(SQLiteDatabase _db)
        {
            _db.execSQL(BEREADER_TABLE_CREATE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase _db, int oldVersion, int newVersion)
        {
            //TODO: Implement this method
        }
    }
}

Come sempre vediamo di analizzarla per spezzoni:

public class DatabaseBeReader
{
    SQLiteDatabase mDb;
    DbHelper mDbHelper;
    Context mContext;
    private static final String DB_NAME="bereader_db";
    private static final int DB_VERSION=1;

    public DatabaseBeReader(Context ctx)
    {
        mContext=ctx;
        mDbHelper = new DbHelper(ctx, DB_NAME, null, DB_VERSION);
    }

    static class BeReaderMetaData
    {
        static final String BEREADER_TABLE = "bereader_table";
        static final String ID = "_id";
        static final String TIPOLOGIA = "tipologia";
        static final String FORMATO = "formato";
        static final String TITOLO = "titolo";
        static final String AUTORE = "autrre";
        static final String GENERE = "genere";
        static final String POSIZIONE = "posizione";
        static final String PRESTATO = "prestato";
        static final String PRESTATO_CHI = "prestato_chi";
        static final String PRESTATO_QUANDO = "prestato_quando";
    }

    private static final String BEREADER_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS "
        + BeReaderMetaData.BEREADER_TABLE + " ("
        + BeReaderMetaData.ID+ " integer primary key autoincrement, "
        + BeReaderMetaData.TIPOLOGIA+  " text not null, "
        + BeReaderMetaData.FORMATO+  " text not null, "
        + BeReaderMetaData.TITOLO+ " text not null, "
        + BeReaderMetaData.AUTORE+ " text,"
        + BeReaderMetaData.GENERE+ " text,"
        + BeReaderMetaData.POSIZIONE+ " text,"
        + BeReaderMetaData.PRESTATO+ " text not null,"
        + BeReaderMetaData.PRESTATO_CHI+ " text,"
        + BeReaderMetaData.PRESTATO_QUANDO + " text"
        +");";

Iniziamo subito con del codice che sembra difficile invece è di facile lettura. Dopo aver istanziato i nostri oggetti che utilizzeremo in tutta la classe e il costruttore (che crea un oggetto della sottoclasse finale), creiamo una sottoclasse statica (ma non privata quindi richiamabile anche fuori dalla classe DatabaseBeReader). Al suo interno definiamo delle costanti sotto definisco il nome dei campi della nostra tabella (avremmo potuto creare delle stringhe ma come vedremo più avanti il codice diventa più immediato).
Nella costante stringa successiva prepariamo la query SQL atta alla creazione della tabella, con tanto di definizione del tipo e degli eventuali modificatori dei campi. Come sempre in SQL facciamo attenzione a virgole, apici, parentesi e punti e virgola.
Definiamo quindi il metodi che servono per aprire e chiudere la connessione al Database.

public void open()
  {
    mDb = mDbHelper.getWritableDatabase();
  }
  
  public void close()
  {
    mDb.close();
  }

E subito dopo ci occupiamo dei metodi di inserimento, lettura, modifica e cancellazione delle registrazioni

public void inserisci(String tipologia, String formato, String titolo,String autore, String genere, String posizione,String prestato, String prestato_chi, String prestato_quando)
    {
        ContentValues cv = new ContentValues();
        cv.put(BeReaderMetaData.TIPOLOGIA, tipologia);
        cv.put(BeReaderMetaData.FORMATO, formato);
        cv.put(BeReaderMetaData.TITOLO, titolo);
        cv.put(BeReaderMetaData.AUTORE, autore);
        cv.put(BeReaderMetaData.GENERE, genere);
        cv.put(BeReaderMetaData.POSIZIONE, posizione);
        cv.put(BeReaderMetaData.PRESTATO, prestato);
        cv.put(BeReaderMetaData.PRESTATO_CHI, prestato_chi);
        cv.put(BeReaderMetaData.PRESTATO_QUANDO, prestato_quando);
        mDb.insert(BeReaderMetaData.BEREADER_TABLE, null, cv);
    }

    public Cursor fetch()
    {
        return mDb.query(BeReaderMetaData.BEREADER_TABLE, null, null, null, null, null, null);
    }

    public Cursor fetchByTipologia(String tipologia)
    {
        return mDb.query(BeReaderMetaData.BEREADER_TABLE, null, BeReaderMetaData.TIPOLOGIA+"='"+tipologia+"'", null        , null, null, null);
    }

    public Cursor fetchById(String _id)
    {
        return mDb.query(BeReaderMetaData.BEREADER_TABLE, null, BeReaderMetaData.ID+"='"+_id+"'", null, null, null,        null);
    }

    public void editById(String id, String campo, String nuovo_valore)
    {
        String editQuery = "UPDATE "+ BeReaderMetaData.BEREADER_TABLE+" SET "+campo+" = '"+nuovo_valore+"' WHERE "+        BeReaderMetaData.ID+"= '"+id+"';";
        mDb.execSQL(editQuery);
    }

    public void cancellaById(String id)
    {
        String deleteQuery = "DELETE FROM "+BeReaderMetaData.BEREADER_TABLE+" WHERE "+BeReaderMetaData.ID+"='" + id        + "';";
        mDb.execSQL(deleteQuery);
    }

La più difficile da leggere è l’inserimento. Istanziamo un oggetto ContentValue, dove inseriamo (put) i valori presi come argomento campo per campo, dopo di che facciamo un insert nel db.
La lettura, la modifica e la cancellazione sono semplici query SQL che tratteremo al momento dell’utilizzo.
Eccoci adesso alla sotto classe più importante il dbHelper:

private class DbHelper extends SQLiteOpenHelper
    {
        public DbHelper (Context context, String name, CursorFactory factory, int version)
        {
            super(context, name, factory, version);
        }

        @Override
        public void onCreate(SQLiteDatabase _db)
        {
            _db.execSQL(BEREADER_TABLE_CREATE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase _db, int oldVersion, int newVersion)
        {
            //TODO: Implement this method
        }
    }

Tale classe si occupa della gestione del database creazione e aggiornamento compresi. Implementeremo l’aggiornamento più avanti.
Sicuramente avete notato che nella classe DatabaseBeReader richiamiamo metodi della classe DbHelper che non abbiamo definito. Avendo esteso la classe SQLiteOpenHelper ne ereditiamo tutti i metodi, notiamo anche che abbiamo riscritto (@Override) i metodi onCreate e onUpgrade (per ora vuoto).

Teoria

Ripassiamo l’anatomia della nostra Applicazione

Abbiamo un’activity principale MainActivity contenente un Navigation Drawer e un FragmentLayout. Il FragmentLayout cambia contenitore ogni volta che viene selezionata una voce nel Drawer. Abbiamo cinque scelte quindi ci servono cinque classi che estendono Fragment. Per adesso ne abbiamo gestite solo due HomePage e Libri.
Home page per ora la lasciamo vuota e ci dedichiamo a Libri.
Libri (come gli altri Fragment) ci presenterà una lista dei nostri inserimenti, filtrati ovviamente per categoria. All’activity aggiungeremo un Floating Action Button che aprirà una nuova Activity (tale Activity avrà un tema diverso dalle altre che la renderà a tutti gli effetti un Dialog) la quale ci permetterà di inserire i dati. Il nostro Fragment Libri quando non abbiamo dati ha questo aspetto:

corso programmazione android lezione 6

Pratica

Il nuovo layout di LibriFragmet

Ecco il nuovo file librifragment.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   <ListView
        android:id="@+id/libri_lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <android.support.design.widget.FloatingActionButton
         android:id="@+id/fb_add_libri"
         android:src="@mipmap/ic_add_white"
         app:fabSize="normal"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="bottom|right"
         android:layout_marginBottom="16dp"
         android:layout_marginEnd="10dp"/>
</android.support.design.widget.CoordinatorLayout>

Definiamo il layout e una ListView, cioè un componente adatto alla visualizzazione di liste, per non complicare ulteriolmente le cose ho scelto il componente più semplice, più avanti ne sceglieremo uno più stiloso.
Il pulsante FAB lo conosciamo già quindi lo salto a pie pari.

Teoria

Activity e Intent

Per aprire una nuova Activity dobbiamo fare affidamento agli Intent. Ecco la definizione di Intent (fonte anddev.it):

Intent ed Intent Filters
Android usa una classe speciale chiamata un Intent per spostarsi da schermata a schermata. Un intent descrive cosa un’applicazione vuole che venga eseguito. Le due parti più importanti della struttura di dati intent sono l’azione e i dati su cui agire. Valori tipici per action sono MAIN (la porta principale dell’applicazione), VIEW, PICK, EDIT, ecc. Il dato è espresso come un URL. Per esempio, per visualizzare informazioni sui contatti di una persona, dovresti creare un intent con l’azione VIEW e il dato impostato su un URL che rappresenti quella persona.

Esiste una classe collegata chiamata un IntentFilter. Mentre un intent è effettivamente una richiesta di fare qualcosa, un intent filter è una descrizione di quanti intent (o quanti intent receiver, sarà più chiaro in seguito) un’activity è capace di gestire. Un’attività che è in grado di mostrare informazioni sui contatti di una persona pubblicherà un IntentFilter che specifica che è in grado di gestire l’azione VIEW quando applicata a dati che rappesentano una persona. Le Activities pubblicano IntentFilters nel file AndroidManifest.xml.

La navigazione da una schermata all’altra viene eseguita risolvendo intents. Per proseguire nella navigazione, un’activity richiama startActivity(myIntent). Il sistema guarda gli intent filters per tutte le applicazioni e sceglie l’activity il cui intent filters si accorda meglio con myIntent. La nuova activity viene informata dell’intent, causandone il lancio. Il processo di risoluzione degli intents avviene nel run time, quando viene richiamata una startActivity, cosa che offre due benefici:

  • Le Activities riusano le funzionalità da altri componenti semplicemente espletando una richiesta nella forma di un intent
  • Le Activities possono essere sostituite in ogni momento da una nuova Activity con un IntentFilter equivalente

Quindi un Intent è un componente importante e specifica se la nostra Activity ha “poteri speciali”, questa funzionalità è utilissima per il riuso. Mettiamo di voler creare una piccola applicazione che dato il contenuto di una TextView invii un SMS con la data di inizio malattia al mio datore di lavoro. Senza gli Intent dovrei riscrivermi da zero tutta la parte relativa l’invio degli SMS, grazie agli Intent posso richiamare l’intent filter apposito e non preoccuparmi di altro (quando utilizzando il telefono vi trovate davanti a una schemata che vi chiede di scegliere quale applicazione usare per gestire una determinata operazione siete davanti a un Intent Filter e a più applicazioni con la stessa Action).
Android utilizza gli Intent e gli Intent Filter anche per il passaggio da un’Activity all’altra (con possibile passaggio di parametri).

Pratica

La nuova Activity AggiungiLibri

E’ il momento di creare una nuova Activity, abbiamo due metodi:

  • tramite la funzione di Android Studio File -> New -> Activity
  • tramite la funzione di Android Studio File -> New -> Java Class

La prima funzionalità è preferibile in quanto ci crea direttamente il file di layout e definisce l’activity nel file Manifest File (parleremo più sotto di tale file). Creiamo quindi una nuova activity chiamata AggiungiLibri, con lo scopo di gestire la finestra di inserimento e di inserire i dati nel db.
Partiamo come sempre analizzando il layout. Android Studio quando crea il layout lo divide in due file, vediamo il primo: activity_aggiungi_libri.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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:fitsSystemWindows="true"
    tools:context=".AggiungiLibri">
 
    <include layout="@layout/content_aggiungi_libri" />


</RelativeLayout>

Come possiamo vedere non ci servono le decorazioni standard di una activity quindi niente actionbar.

Vediamo il secondo e più corposo file: content_aggiungi_libri.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context=".AggiungiLibri"
    tools:showIn="@layout/activity_aggiungi_libri">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv1_libri"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/titolo"/>
            <EditText
                android:id="@+id/et_titolo"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/autore"/>
            <EditText
                android:id="@+id/et_autore"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/formato"/>
            <Spinner
                android:id="@+id/spinner_formato"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:spinnerMode="dropdown" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/genere"/>
            <EditText
                android:id="@+id/et_genere"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/posizione"/>
            <EditText
                android:id="@+id/et_posizione"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/prestato"/>
            <Spinner
                android:id="@+id/spinner_prestato"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:spinnerMode="dropdown" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/prestato_chi"/>
            <EditText
                android:id="@+id/et_prestato_chi"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/prestato_quando"/>
            <EditText
                android:id="@+id/et_prestato_qaundo"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </ScrollView>

    <ImageButton
        android:id="@+id/ib_salva"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:text="@string/salva"
        android:src="@mipmap/ic_action_save"/>

</RelativeLayout>

Abbiamo definito una serie di componenti fra i quali due Spinner, notiamo che il componente ImageButton essendo fisso è fuori dalla ScroolView.
Per associare le stringhe allo Spinner dobbiamo intervenire via codice ma dobbiamo prima creare un file array.xml con il seguente contenuto:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="ar_formato_libri">
        <item>@string/cartaceo</item>
        <item>@string/ebook</item>
    </array>
    <array name="ar_si_no">
        <item>@string/si</item>
        <item>@string/no</item>
    </array>
</resources>

Ovviamente dobbiamo aver definito le stringhe all’interno del file strings.xml (come per le stringhe usate nei layout)l:

.... 
<string name="cartaceo">Cartaceo</string>
<string name="ebook">Ebook</string> 
<string name="si">Si</string> 
<string name="no">No</string> 
.....

Teoria

Il Manifest file

Per fa si che la nostra Activity somigli a un Dialog dobbiamo associarle un Tema specifico. Per farlo useremo il file AndroidManifest.xml. Tale file si occupa, tra l’altro, di dichiarare tutte le Activty e di gestire gli Intent.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.begeekmyfriend.bereader">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".AggiungiLibri"
            android:label="@string/title_activity_aggiungi_libri" android:theme="@style/Theme.AppCompat.Dialog">>
        </activity>
    </application>

</manifest>

MainActivity è segnalata come Activity principale e grazie alla categoria LAUNCHER è la prima ad essere richiamata all’avvio dell’applicazione.
Ecco l’aspetto della nostra Activity:

corso programmazione android lezione 6

 

Pratica

Il codice Java di AggiungiLibri

Come sempre vediamo il codice completo:

package com.begeekmyfriend.bereader;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Spinner;


public class AggiungiLibri extends Activity {

    //Defnisco istanze degli oggetti
    EditText et_titolo, et_autore, et_genere, et_posizione, et_prestato_chi, et_prestato_quando;
    Spinner spinner_formato, spinner_prestato;
    ImageButton button_salva;
    ArrayAdapter <CharSequence> aa_formato, aa_prestato;
    DatabaseBeReader db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aggiungi_libri);
        et_titolo = (EditText) findViewById(R.id.et_titolo); //Associo id all'oggetto
        et_autore = (EditText) findViewById(R.id.et_autore);
        spinner_formato = (Spinner) findViewById(R.id.spinner_formato);
        //Creo un array adapter utilizzando l'array di stringhe
        aa_formato = ArrayAdapter.createFromResource(this, R.array.ar_formato_libri,
                android.R.layout.simple_spinner_item);
        //Associo un layout di sistema
        aa_formato.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner_formato.setAdapter(aa_formato); //Associo l'adapter allo spinner
        spinner_prestato = (Spinner) findViewById(R.id.spinner_prestato);
        et_genere = (EditText) findViewById(R.id.et_genere);
        et_posizione = (EditText) findViewById(R.id.et_posizione);
        aa_prestato = ArrayAdapter.createFromResource(this, R.array.ar_si_no, android.R.layout.simple_spinner_item);
        aa_prestato.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner_prestato.setAdapter(aa_prestato);
        et_prestato_chi = (EditText) findViewById(R.id.et_prestato_chi);
        et_prestato_quando = (EditText) findViewById(R.id.et_prestato_qaundo);
        button_salva = (ImageButton) findViewById(R.id.ib_salva);
        button_salva.setOnClickListener(new View.OnClickListener() { //Classe anonima per il clicj dell'image button
            @Override
            public void onClick(View v) {
                salva(); //Richiamo metodo per il salvataggio
            }
        });
    }

    public void salva(){
        db = new DatabaseBeReader(this); //Denisco istanza dell'oggetto db e richiamo il costruttore
        db.open(); //Apro il db
        //Inserisco nel db il contenuto dei campi
        db.inserisci(getResources().getString(R.string.libri), spinner_formato.getSelectedItem().toString(),
                et_titolo.getText().toString(), et_autore.getText().toString(), et_genere.getText().toString(),
                et_posizione.getText().toString(), spinner_prestato.getSelectedItem().toString(), et_prestato_chi.getText().toString(),
                et_prestato_quando.getText().toString());
        db.close(); //Chiudo il db
        finish(); //chiudo il dialog
     }

}

Abbiamo già visto più o meno tutti i componenti quindi concentriamoci solo sugli Spinner e sul metodo salva().

spinner_formato = (Spinner) findViewById(R.id.spinner_formato);
//Creo un array adapter utilizzando l'array di stringhe
aa_formato = ArrayAdapter.createFromResource(this, R.array.ar_formato_libri,
                android.R.layout.simple_spinner_item); //Associo un layout di sistema
aa_formato.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_formato.setAdapter(aa_formato); //Associo l'adapter allo spinner

Il metodo più importante è ArrayAdapter.createFromResources e ci permette di leggere una risorsa, nello specifico uno degli array di stringhe che abbiamo inserito nel file array.xml, via codice.
Occupiamoci adesso del metodo salva():

public void salva(){
    db = new DatabaseBeReader(this); //Definisco istanza dell'oggetto db e richiamo il costruttore
    db.open(); //Apro il db
    //Inserisco nel db il contenuto dei campi
    db.inserisci(getResources().getString(R.string.libri), spinner_formato.getSelectedItem().toString(),
            et_titolo.getText().toString(), et_autore.getText().toString(), et_genere.getText().toString(),
            et_posizione.getText().toString(), spinner_prestato.getSelectedItem().toString(), et_prestato_chi.getText().toString(),
            et_prestato_quando.getText().toString());
    db.close(); //Chiudo il db
    finish(); //chiudo il dialog
 }

Questo metodo (richiamato dopo aver cliccato il bottone) si occupa di creare l’oggetto db, aprire connessione al Database, inserirvi quanto scritto all’interno della videata anche qui richiamando una risorsa) e di chiudere connessione e activity.

La nuova classe LibriFragment

Ecco quindi il nuovo codice commentato della classe LibriFragment:

package com.begeekmyfriend.bereader;

import android.app.Fragment;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;

/**
 * Created by fabrizio on 12/03/16.
 */
public class LibriFragment extends Fragment{

    private ListView libriLV;
    DatabaseBeReader db;
    Cursor c;
    SimpleCursorAdapter cur;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.libri_fragment, container, false);
        libriLV = (ListView) rootView.findViewById(R.id.libri_lv);
        db = new DatabaseBeReader(rootView.getContext());  //istanzio un oggetto DatabaseReader
        db.open(); //apro la connessione con il db
        c = db.fetchByTipologia(getResources().getString(R.string.libri)); //eseguo la query per tipologia
        //istanzio l'oggetto Cursor inserendovi il risultato della query
        cur = new SimpleCursorAdapter( //Creo un Adapter per il cursore definindo quali campi deve mostrare 
            getActivity(),
            android.R.layout.simple_list_item_2,
            c,
            new String[] {DatabaseBeReader.BeReaderMetaData.TITOLO, DatabaseBeReader.BeReaderMetaData.AUTORE},
            new int[]{android.R.id.text1 , android.R.id.text2},
            0);
        libriLV.setAdapter(cur);
        db.close();
        FloatingActionButton fab = (FloatingActionButton) rootView.findViewById(R.id.fb_add_libri);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getActivity(), AggiungiLibri.class);
                startActivity(intent);
            }
        });

        return rootView;
    }
}

Salvo i commenti notiamo che il SimpleCursorAdapter usa un layout di sistema (android.R.layout) usando gli id propri di tale layout.
Eccoci all’Intent:

fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getActivity(), AggiungiLibri.class);
                startActivity(intent);
            }
        });

Ecco finalmente il nostro Fragment pronto a mostrarci i dati che abbiamo inserito:

Nelle prossime lezioni vedremo di migliorare l’aspetto generale dell’applicazione.

Originariamente pubblicato su Be Geek My Friend.

You May Have Missed