Corso programmazione Android Lezione 14: SimpleCursorAdapter personalizzato e query SQL complesse

corso programmazione android lezione 14

Corso programmazione Android Lezione 14: SimpleCursorAdapter personalizzato e query SQL complesse

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

Nella prima parte di questa lezione del corso vedremo come creare un SimpleCursorAdapter personalizzato che ci permetterà di mostrare più di due campi del DB e di inserire un’immagine.
I sorgenti sono disponibili qui.

SimpleCursorAdapter personalizzato

Layout

Partiamo subito creando un layout, vediamo il file xml corrispondente (/layout/cursor_per_layout)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:background="@drawable/border_layout">
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="10">
        <ImageView
            android:id="@+id/raw31"
            android:layout_width="65sp"
            android:layout_height="65dp"
            android:src="@drawable/book"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_weight="90"
        android:layout_gravity="center">
        <TextView
            android:id="@+id/raw11"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAlignment="center"
            android:textStyle="bold"
            android:textSize="20sp"
            android:layout_gravity="center"/>
        <TextView
            android:id="@+id/raw21"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAlignment="center"
            android:textSize="20sp"
            android:layout_gravity="center"/>
        <TextView
            android:id="@+id/raw41"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAlignment="center"
            android:textSize="20sp"
            android:layout_gravity="center"/>
    </LinearLayout>
</LinearLayout>

Abbiamo 4 componenti, un’immagine (raw31) che occupa 10 unità di spazio e tre TextView (raw11, 21 e 41) che una sotto all’altra occupano 90 unità.

Codice Java

Passiamo al codice Java della classe PersCursor (file Perscursor.java) che come possiamo vedere estende SimpleCursorAdapter e quindi ne eredita tutti gli oggetti e i metodi.

package com.begeekmyfriend.bereader;

import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;

/**
 * Created by fabrizio on 10/07/16.
 */
 
public class PersCursor extends SimpleCursorAdapter {

    private Cursor c;
    private Context context;

  //Inizializzo variabili
    String name="";
    String type="";
    String type2="";
    String type3="";
    int layout;
    int intname=0;
    int inttype=0;
    int inttype2=0;
    int inttype3=0;
    int tipologia;

    public PersCursor(Context context, int layout, Cursor c, String[] from, int[] to, int flags) { //Costruttore 
        super(context, layout, c, from, to, flags);
        this.c = c;
        this.context = context;
        this.layout =layout;
        this.name=from[0];
        this.type=from[1];
        this.type2=from[2];
        this.type3=from[3];
        this.intname=to[0];
        this.inttype=to[1];
        this.inttype2=to[2];
        this.inttype3=to[3];
    }

    public View getView(int pos, View inView, ViewGroup parent) {  //metodo di callback
        View v = inView;
        if (v == null) { //Associo il layout passato come argomento
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = inflater.inflate(layout, null);
        }
        this.c.moveToPosition(pos); //gestisco il record del cursore
        Inserisco il risultato nelle stringhe
    String name = this.c.getString(this.c.getColumnIndex(this.name)); 
        String type = this.c.getString(this.c.getColumnIndex(this.type));
        String type2;
        type2 = this.c.getString(this.c.getColumnIndex(this.type2));
        String type3;
        if (this.type3.equals(null))
        {
            type3 = "";
        }
        else
        {
            type3 = this.c.getString(this.c.getColumnIndex(this.type3));
        }
    //Inizializzo i componenti del layout e ci inserisco i dati
        TextView fname = (TextView) v.findViewById(intname);
        fname.setText(name);
        TextView ltype = (TextView) v.findViewById(inttype);
        ltype.setText(type);
        ImageView ltype2 = (ImageView) v.findViewById(inttype2);
        ltype2.setImageResource(R.drawable.book);
        if (this.type2.equals(DatabaseBeReader.BeReaderMetaData.TIPOLOGIA))//Gestisco l'immagine
        {
            switch (type2)
            {
                case "01":
                    ltype2.setImageResource(R.drawable.book);
                    break;
                case "02":
                    ltype2.setImageResource(R.drawable.comic);
                    break;
                case "03":
                    ltype2.setImageResource(R.drawable.disc);
                    break;
            }

        }
        TextView ltype3 = (TextView) v.findViewById(inttype3);
        if (inttype3 != 0) {
            if (this.type3.equals(DatabaseBeReader.BeReaderMetaData.TIPOLOGIA)) //Trucchetto per nascondere la terza stringa se non ci interessa
            {
                ltype3.setVisibility(View.GONE);
            }
            else
            {
                ltype3.setText(type3);
            }
        }
        return (v); //Restituisco la View
    }

}

Il codice è relativamente semplice e consiste di un costruttore e di un metodo (gli altri metodi della superclasse SimplecursorAdapter gli ereditiamo senza modifiche).
Il metodo getView si occupa di definire i componenti passati al costruttore e di posizionarvi i dati contenuti nel Cursor. Per l’immagine abbiamo usato un trucco che ci permette di sceglierla in base alla tipologia di registrazione.
La terza TextView è opzionale quindi se vi passiamo la tipologia (già passata precedentemente) viene nascosto, grazie al metodo ltype3.setVisibility(View.GONE);.
Questo nuovo cursore è stato applicato a tutte le ListView che leggono dalla tabella principale, vediamo che modifiche servono al codice (ho preso come esempio la classe HomepageFragment), leggendo la porzione corrispondente:

.
.
.

 PersCursor cur, cur_resume;

.
.
.
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
cur = new PersCursor(
                getActivity(),
                R.layout.cursor_pers_layout,
                c,
                new String[]{DatabaseBeReader.BeReaderMetaData.TITOLO, DatabaseBeReader.BeReaderMetaData.AUTORE, DatabaseBeReader.BeReaderMetaData.TIPOLOGIA,DatabaseBeReader.BeReaderMetaData.GENERE},
                new int[]{R.id.raw11, R.id.raw21, R.id.raw31, R.id.raw41},
                0);
        homeLV.setAdapter(cur);
.
-
-
}

Dopo le nostre modifiche la Homepage risulta più godibile e sopratutto riusciamo a distinguere le tipologie una dall’altra.

corso programmazione android lezione 14

A titolo esemplificativo nelle liste dove conosco già la tipologia (LibriFragment, FumettiFragment e DischiFragment) ho provato a mostrare solo 3 campi, pur mantenendo l’immagine, vediamo come:

cur = new PersCursor(
            getActivity(),
      R.layout.cursor_pers_layout,
      c,
      new String[] {DatabaseBeReader.BeReaderMetaData.TITOLO, DatabaseBeReader.BeReaderMetaData.AUTORE, DatabaseBeReader.BeReaderMetaData.TIPOLOGIA, DatabaseBeReader.BeReaderMetaData.TIPOLOGIA},
      new int[]{R.id.raw11 , R.id.raw21, R.id.raw31, R.id.raw41},
      0);
        libriLV.setAdapter(cur);

Come definito nella classe PersCursor se nel quarto campo reinseriamo la tipologia esso scompare, vediamo come risulta l’aspetto

corso programmazione android lezione 14

Query SQL Complesse

Adesso che ci siamo creati un Cursor Personalizzato possiamo gestire la lista che ci mostra gli elementi che abbiamo prestato (qui ci servono tre elementi di testo).
In tale Fragment vogliamo visualizzare campi presenti in entrambe le tabelle e quindi dobbiamo incrociarle.

Ripasso

Abbiamo due tabelle una contente tutti i dati relativi all’elemento e una relativa ai prestiti, vediamo la definizione:

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" //,"
        +");";

private static final String BEREADER_PRES_TABLE_CREATE = "CREATE TABLE IF NOT EXISTS "
        + BeReaderPresMetaData.BEREADER_PRES_TABLE + " ("
        + BeReaderPresMetaData.ID+ " integer primary key autoincrement, "
        + BeReaderPresMetaData.ID_BER+ " integer, "
        + BeReaderPresMetaData.PRESTATO_CHI+ " text,"
        + BeReaderPresMetaData.PRESTATO_QUANDO + " text,"
        + BeReaderPresMetaData.PRESTATO_FINE + " text"
        +");";

Nelle precedenti lezioni abbiamo visto che nel campo ID_BER della tabella PresTable inseriamo l’id del libro, fumetto o disco al quale vogliamo associare il prestito.

All’opera

Android ci permette d utilizzare il metodo rawQuery per creare query complesse, vediamo la nostra:

public Cursor fetchByBerPres()
    {
        //Incrocio le due tabelle per ID ed eseguo una query
        String query = "SELECT "
            +"*"
            +" FROM "+ BeReaderMetaData.BEREADER_TABLE+ " a INNER JOIN "
            + BeReaderPresMetaData.BEREADER_PRES_TABLE
            +" b ON a."+BeReaderMetaData.ID
            + "= b."+BeReaderPresMetaData.ID_BER
            + " ORDER BY a."+BeReaderMetaData.TITOLO
            +";";
        return mDb.rawQuery(query,null);
    }

Qui selezioniamo tutto dalla tabella temporanea che si viene a creare facendo un INNER JOIN di due registrazioni se ID e ID_BER sono identici, come sempre ordiniamo per titolo.
Vediamo per intero il codice della classe PresFragment:

ackage com.begeekmyfriend.bereader;

import android.app.Fragment;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
//import android.widget.SimpleCursorAdapter;
import android.widget.TextView;

/**
 * Created by fabrizio on 02/06/16.
 */
public class PrestitiFragment extends Fragment {
    DatabaseBeReader db;
    ListView presLV;
    Cursor c, c1;
    PersCursor cur, cur_resume;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_prestiti, container, false);
        presLV = (ListView) rootView.findViewById(R.id.pres_lv);
        db = new DatabaseBeReader(getActivity());
        db.open();
        c = db.fetchByBerPres();
        //Cursor merCur = new MergeCursor (new Cursor[]{c, c1});
        cur = new PersCursor(
                getActivity(),
                R.layout.cursor_pers_layout,
                c,
                new String[]{DatabaseBeReader.BeReaderMetaData.TITOLO, DatabaseBeReader.BeReaderPresMetaData.PRESTATO_CHI,DatabaseBeReader.BeReaderMetaData.TIPOLOGIA,  DatabaseBeReader.BeReaderPresMetaData.PRESTATO_QUANDO},
                new int[]{R.id.raw11, R.id.raw21, R.id.raw31, R.id.raw41},
                0);
        presLV.setAdapter(cur);
        presLV.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                db.open();
                int tipol = 1; //Costante per lo switch
                String tipologia = c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.TIPOLOGIA));
                //Associo una costante alla tipologia
                if (tipologia.equals("02")) tipol = 2;
                if (tipologia.equals("03")) tipol = 3;
                Intent intent;
                switch (tipol) //Ogni tipologia ha la sua Activity
                {
                    case 1:
                        intent = new Intent(getActivity(), VisualizzaLibri.class);
                        break;
                    case 2:
                        intent = new Intent(getActivity(), VisualizzaFumetti.class);
                        break;
                     case 3:
                        intent = new Intent(getActivity(), VisualizzaDischi.class);
                         break;
                    default:
                        intent = new Intent(getActivity(), VisualizzaLibri.class);
                        break;
                }
                //Intent intent = new Intent(getActivity(), VisualizzaFumetti.class); //apro una nuova activity
                String pkg = getActivity().getPackageName();
                //Passo come parametro alla nuova Activity l'id della registrazione
                intent.putExtra(pkg+".ID",  c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderPresMetaData.ID_BER)));
                startActivity(intent);
            }
        });
        TextView prestiti_empty = (TextView) rootView.findViewById(R.id.prestiti_empty);
        presLV.setEmptyView(prestiti_empty);
        return rootView;
    }

    @Override
    public void onResume()
    {
        super.onResume();
        db.open();
        c =  db.fetchByBerPres();
        cur_resume = new PersCursor(
                getActivity(),
                R.layout.cursor_pers_layout,
                c,
                new String[]{DatabaseBeReader.BeReaderMetaData.TITOLO, DatabaseBeReader.BeReaderPresMetaData.PRESTATO_CHI, DatabaseBeReader.BeReaderMetaData.TIPOLOGIA, DatabaseBeReader.BeReaderPresMetaData.PRESTATO_QUANDO},
                new int[]{R.id.raw11, R.id.raw21, R.id.raw31, R.id.raw41},
                0);
        presLV.setAdapter(cur_resume);
        db.close();
    }
}

Qui mostriamo i campi titolo, tipologia (inseriti nella tabella BE_READER), prestito_chi e prestato_quando (inseriti invece nella tabella PRES_BE_READER) ecco il risultato:

corso programmazione android lezione 14

Per oggi abbiamo finito, nella prossima lezione internazionalizzeremo la nostra APP, che ormai è pronta per essere pubblicata sul Play Store.

Originariamente pubblicato su Be Geek My Friend

You May Have Missed