Corso Programmazione Android Lezione 10: DialogFragment e CoordinatorLayout

Corso Programmazione Android Lezione 10: DialogFragment e CoordinatorLayout

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

In questa lezione vedremo la creazione di un Dialog Fragment e alcuni piccolo effetti offerti dal CoordinatorLayout.
Il codice sorgente mostrato in questa lezione è reperibile qui.

Pratica

DialogFragment

Per la modifica delle nostre registrazioni utilizzeremo il componente Dialog e nello specifico un DialogFragment per avere un’app più moderna.

Modifichiamo il nostro layout di visualizzazione

Ho previsto la modifica tramite click sull’elemento, quindi, come sappiamo, dobbiamo associare un id al componente per richiamarlo da codice Java. Il file interessato è content_visualizza.xml, ho modificato leggermente il layout non mi piacevano il titolo e l’autore affiancati. Il file è piuttosto lungo quindi ve lo risparmio vediamone solo un pezzo:

<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/paper_tile"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <LinearLayout
            android:id="@+id/ll_tit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:layout_marginBottom="5dp"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout">
            <TextView
                android:id="@+id/titolo_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/titolo"/>
            <TextView
                android:id="@+id/titolo_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>

Avrete sicuramente notato la presenza di un nuovo elemento NestedScrollView lo vedremo più avanti quando analizzeremo CoordinatorLayout, Ogni LinearLayout rappresentante un quadratino adesso ha il suo id che potremo utilizzare all’interno del codice Java. Per rinfrescarci la memoria vi mostro l’aspetto del layout (visto che l’ho modificato):

coro programmazione android lezione 10

Vediamo adesso l’intero codice Java della classe VislibriTab1 (leggermente ingigantito) opportunamente commentato:

package com.begeekmyfriend.bereader;

import android.database.Cursor;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;

/**
 * Created by fabrizio on 17/04/16.
 */
public class VisLibriTab1 extends Fragment
{
    private static final String ARG_SECTION_NUMBER = "section_number";
    static DatabaseBeReader db;
    Cursor c, c_new;
    static String id_db;
    static TextView titolo_tv, autore_tv, formato_tv, genere_tv, posizione_tv, prestato_tv, prestato_chi_tv, prestato_quando_tv;
    FloatingActionButton fab_del;
    LinearLayout ll_tit, ll_aut, ll_for, ll_gen, ll_pos, ll_pres, ll_pres_c, ll_pres_q;

    public VisLibriTab1() { //Inutile costruttore di default
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,   Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View rootView = inflater.inflate(R.layout.fragment_vis_tab1, container, false);
        Bundle args = getArguments();
        id_db = args.getString("ID"); //Recuperiamo l'id passatoci dall'Activity
        //Inizializzo tutti i TetxVew adibiti al contenimento dei dati
        titolo_tv = (TextView) rootView.findViewById(R.id.titolo_tv);
        autore_tv = (TextView) rootView.findViewById(R.id.autore_tv);
        formato_tv = (TextView) rootView.findViewById(R.id.formato_tv);
        genere_tv = (TextView) rootView.findViewById(R.id.genere_tv);
        posizione_tv = (TextView) rootView.findViewById(R.id.posizione_tv);
        prestato_tv = (TextView) rootView.findViewById(R.id.prestato_tv);
        prestato_chi_tv = (TextView) rootView.findViewById(R.id.prestato_chi_tv);
        prestato_quando_tv = (TextView) rootView.findViewById(R.id.prestato_quando_tv);
        db = new DatabaseBeReader(getActivity()); //Istanzio l'oggetto 
        db.open(); //Apro la connesione al db
        c = db.fetchById(id_db); //Lancio la query By id
        if ( c.getCount() > 0) //Dopo aver aperto il db inizializza i componenti
         {
             while (c.moveToNext()) { //anche se ho sicuramente un solo recordo mi serve un ciclo
                    //scrivo il valore del campo del recordo all'interno della TextView
                    titolo_tv.setText(c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.TITOLO)));
                    autore_tv.setText(c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.AUTORE)));
                    formato_tv.setText(c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.FORMATO)));
                    genere_tv.setText(c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.GENERE)));
                    posizione_tv.setText(c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.POSIZIONE)));
                    prestato_tv.setText(c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.PRESTATO)));
                    prestato_chi_tv.setText(c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.PRESTATO_CHI)));
                    prestato_quando_tv.setText(c.getString(c.getColumnIndex(DatabaseBeReader.BeReaderMetaData.PRESTATO_QUANDO)));
             }
         }
        // Inizializzo i componenti per la modifica e richiamo metodo creato appositamente
        ll_tit = (LinearLayout) rootView.findViewById(R.id.ll_tit);
        ll_tit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                modificaRegistrazioni(0,DatabaseBeReader.BeReaderMetaData.TITOLO, titolo_tv.getText());
            }
        });
        ll_aut = (LinearLayout) rootView.findViewById(R.id.ll_aut);
        ll_aut.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                modificaRegistrazioni(0,DatabaseBeReader.BeReaderMetaData.AUTORE, autore_tv.getText());
            }
        });
        ll_for = (LinearLayout) rootView.findViewById(R.id.ll_for);
        ll_for.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                modificaRegistrazioni(1,DatabaseBeReader.BeReaderMetaData.FORMATO, formato_tv.getText());
            }
        });
        ll_gen = (LinearLayout) rootView.findViewById(R.id.ll_gen);
        ll_gen.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                modificaRegistrazioni(0,DatabaseBeReader.BeReaderMetaData.GENERE, genere_tv.getText());
            }
        });
        ll_pos = (LinearLayout) rootView.findViewById(R.id.ll_pos);
        ll_pos.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                modificaRegistrazioni(0,DatabaseBeReader.BeReaderMetaData.POSIZIONE, posizione_tv.getText());
            }
        });
        ll_pres = (LinearLayout) rootView.findViewById(R.id.ll_pres);
        ll_pres.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                modificaRegistrazioni(1,DatabaseBeReader.BeReaderMetaData.PRESTATO, prestato_tv.getText());
            }
        });
        ll_pres_c = (LinearLayout) rootView.findViewById(R.id.ll_pres_c);
        ll_pres_c.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                modificaRegistrazioni(0,DatabaseBeReader.BeReaderMetaData.PRESTATO_CHI, prestato_chi_tv.getText());
            }
        });
        ll_pres_q = (LinearLayout) rootView.findViewById(R.id.ll_pres_q);
        ll_pres_q.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                modificaRegistrazioni(0,DatabaseBeReader.BeReaderMetaData.PRESTATO_QUANDO, prestato_quando_tv.getText());
            }
        });
        db.close(); //chiudo la connessione
        return rootView;
    }


    private void modificaRegistrazioni(int tipo, final String campo, CharSequence oldt1)
    {
        Bundle args = new Bundle();
        FragmentManager fm = getActivity().getSupportFragmentManager(); //Istanzio il fragment manager
        args.putInt("TIPOL", tipo); //Ci serve per sapere se ci serve o l'EditText o Lo Spinner
        args.putString("CAMPO", campo); //Campo da modificare
        args.putCharSequence("STRINGA", oldt1);
        DialogoModifica dialogo = new DialogoModifica(); //Istanzio l'oggetto della classe del Dialog
        dialogo.setArguments(args); //Passo gli argomenti
        dialogo.setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogFragment); //Setto uno stile personalizzato
        dialogo.show(fm, null); //Apro il Dialog
    }

    public static class DialogoModifica extends DialogFragment {

        String campo;
        CharSequence oldt1;
        TextView old_val;
        ImageButton bottone_mod;
        int tipo_layout;

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,   Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //Recupero gli argomenti
            Bundle args = getArguments();
            oldt1 = args.getCharSequence("STRINGA"); 
            campo = args.getString("CAMPO");
            tipo_layout = args.getInt("TIPOL");
            View rootView = null;
            //In base al tipo di layout decido se mi serve il layout con dell'EditText o lo spinner  
            switch (tipo_layout) {
                case 0: //EditText
                    rootView = inflater.inflate(R.layout.dialog_modifica, container, false);
                    TextView label = (TextView) rootView.findViewById(R.id.label_modifica);
                    label.setText("Modifica ".concat(campo.toLowerCase()));
                    old_val = (TextView) rootView.findViewById(R.id.et_oval);
                    old_val.setText(oldt1);
                    final EditText new_val = (EditText) rootView.findViewById(R.id.ed_nval);
                    new_val.setHint(oldt1); //associo un Hint (suggerimento)
                    bottone_mod = (ImageButton) rootView.findViewById(R.id.bottone_modifica);
                    bottone_mod.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            db.open();
                            db.editById(id_db, campo, new_val.getText().toString()); //Modifico la registrazione
                            db.close();
                            //Mi preparo ad aggiornare il layout dopo la modifica
                            switch (campo) {
                                case DatabaseBeReader.BeReaderMetaData.TITOLO:
                                    titolo_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.AUTORE:
                                    autore_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.GENERE:
                                    genere_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.POSIZIONE:
                                    posizione_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.PRESTATO_CHI:
                                    prestato_chi_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.PRESTATO_QUANDO:
                                    prestato_quando_tv.setText(new_val.getText().toString());
                                    break;
                            }
                            getDialog().dismiss(); //Chiudo il Dialog
                        }
                    });
                    //getDialog().setTitle(getString(R.string.modifica));
                    return rootView;
                case 1: //Spinner
                    rootView = inflater.inflate(R.layout.dialog_modifica2, container, false);
                    label = (TextView) rootView.findViewById(R.id.label_modifica);
                    label.setText("Modifica ".concat(campo.toLowerCase()));
                    old_val = (TextView) rootView.findViewById(R.id.et_oval);
                    old_val.setText(oldt1);
                    final Spinner sp_nval = (Spinner) rootView.findViewById(R.id.sp_nval);
                    ArrayAdapter aa = null;
                    //In base al campo setto l'ArrayAdapter e lo spinner con i Giusti valori
                    switch (campo) {
                        case DatabaseBeReader.BeReaderMetaData.FORMATO:
                            //Anche il formato è diverso 
                            if (getActivity().getClass() == VisualizzaDischi.class) {
                                aa = ArrayAdapter.createFromResource(getActivity(), R.array.ar_formato_dischi,
                                        R.layout.spinner);

                            }
                            else {
                                aa = ArrayAdapter.createFromResource(getActivity(), R.array.ar_formato_libri,
                                        R.layout.spinner);
                            }
                                break;
                        case DatabaseBeReader.BeReaderMetaData.PRESTATO:
                                aa = ArrayAdapter.createFromResource(getActivity(), R.array.ar_si_no,
                                        R.layout.spinner);
                                break;

                    }
                    sp_nval.setAdapter(aa);
                    bottone_mod = (ImageButton) rootView.findViewById(R.id.bottone_modifica);
                    bottone_mod.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            db.open();
                            db.editById(id_db, campo, sp_nval.getSelectedItem().toString()); //Modifico la registrazione
                            db.close();
                            //Aggiornamenti del layout post modifica
                            switch (campo) {
                                case DatabaseBeReader.BeReaderMetaData.FORMATO:
                                    formato_tv.setText(sp_nval.getSelectedItem().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.PRESTATO:
                                    prestato_tv.setText(sp_nval.getSelectedItem().toString());
                                    break;
                                default:
                                    break;
                            }
                        getDialog().dismiss(); //Chiudo il Dialog
                        }
                    });
                    default : //Inutile ma il compilatore non lo sa, se non lo metti da errore di compilazione
                        return rootView;
            }
        }
     }
}

Ho voluto creare una sola classe per la visualizzazione di tutte le tipologie quindi ho dovuto fare dei giochetti per rendela riutilizzabile al massimo. Il codice mi sembra ben commentato quindi vediamo solo la classe interna che estende DialogFragment e il metodo che apre il Dialog.

public static class DialogoModifica extends DialogFragment {

        String campo;
        CharSequence oldt1;
        TextView old_val;
        ImageButton bottone_mod;
        int tipo_layout;

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,   Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //Recupero gli argomenti
            Bundle args = getArguments();
            oldt1 = args.getCharSequence("STRINGA");
            campo = args.getString("CAMPO");
            tipo_layout = args.getInt("TIPOL");
            View rootView = null;
            //In base al tipo di layout decido se mi serve il layout con dell'EditText o lo spinner
            switch (tipo_layout) {
                case 0: //EditText
                    rootView = inflater.inflate(R.layout.dialog_modifica, container, false);
                    TextView label = (TextView) rootView.findViewById(R.id.label_modifica);
                    label.setText("Modifica ".concat(campo.toLowerCase()));
                    old_val = (TextView) rootView.findViewById(R.id.et_oval);
                    old_val.setText(oldt1);
                    final EditText new_val = (EditText) rootView.findViewById(R.id.ed_nval);
                    new_val.setHint(oldt1); //associo un Hint (suggerimento)
                    bottone_mod = (ImageButton) rootView.findViewById(R.id.bottone_modifica);
                    bottone_mod.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            db.open();
                            db.editById(id_db, campo, new_val.getText().toString()); //Modifico la registrazione
                            db.close();
                            //Mi preparo ad aggiornare il layput dopo la modifica
                            switch (campo) {
                                case DatabaseBeReader.BeReaderMetaData.TITOLO:
                                    titolo_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.AUTORE:
                                    autore_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.GENERE:
                                    genere_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.POSIZIONE:
                                    posizione_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.PRESTATO_CHI:
                                    prestato_chi_tv.setText(new_val.getText().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.PRESTATO_QUANDO:
                                    prestato_quando_tv.setText(new_val.getText().toString());
                                    break;
                            }
                            getDialog().dismiss(); //Chiudo il Dialog
                        }
                    });
                    //getDialog().setTitle(getString(R.string.modifica));
                    return rootView;
                case 1: //Spinner
                    rootView = inflater.inflate(R.layout.dialog_modifica2, container, false);
                    label = (TextView) rootView.findViewById(R.id.label_modifica);
                    label.setText("Modifica ".concat(campo.toLowerCase()));
                    old_val = (TextView) rootView.findViewById(R.id.et_oval);
                    old_val.setText(oldt1);
                    final Spinner sp_nval = (Spinner) rootView.findViewById(R.id.sp_nval);
                    ArrayAdapter aa = null;
                    //In base al campo setto l'ArrayAdapter e lo spinner con i Giusti valori
                    switch (campo) {
                        case DatabaseBeReader.BeReaderMetaData.FORMATO:
                            //Anche il formato è diverso
                            if (getActivity().getClass() == VisualizzaDischi.class) {
                                aa = ArrayAdapter.createFromResource(getActivity(), R.array.ar_formato_dischi,
                                        R.layout.spinner);

                            }
                            else {
                                aa = ArrayAdapter.createFromResource(getActivity(), R.array.ar_formato_libri,
                                        R.layout.spinner);
                            }
                                break;
                        case DatabaseBeReader.BeReaderMetaData.PRESTATO:
                                aa = ArrayAdapter.createFromResource(getActivity(), R.array.ar_si_no,
                                        R.layout.spinner);
                                break;

                    }
                    sp_nval.setAdapter(aa);
                    bottone_mod = (ImageButton) rootView.findViewById(R.id.bottone_modifica);
                    bottone_mod.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            db.open();
                            db.editById(id_db, campo, sp_nval.getSelectedItem().toString()); //Modifico la registrazione
                            db.close();
                            //Aggiornamenti del layout post modifica
                            switch (campo) {
                                case DatabaseBeReader.BeReaderMetaData.FORMATO:
                                    formato_tv.setText(sp_nval.getSelectedItem().toString());
                                    break;
                                case DatabaseBeReader.BeReaderMetaData.PRESTATO:
                                    prestato_tv.setText(sp_nval.getSelectedItem().toString());
                                    break;
                                default:
                                    break;
                            }
                        getDialog().dismiss(); //Chiudo il Dialog
                        }
                    });
                    default : //Inutile ma il compilatore non lo sa, se non lo metti da errore di compilazione
                        return rootView;
            }
        }

Anche questa classe è un pò gigante e ha dei trucchetti per rendere il tutto riutilizzabile, non ve la descrivo in quanto, come potete vedere, non è altro che un semplice Fragment. Vengono richiamati solo dei metodi che non conosciamo ma dal significato abbastanza intuibile. Il metodo getDialog().dismiss() infatti si usa per chiudere il Dialog quando non ne abbiamo più bisogno.
Dopo la classe il metodo che apre il Dialog:

private void modificaRegistrazioni(int tipo, final String campo, CharSequence oldt1)
    {
        Bundle args = new Bundle();
        FragmentManager fm = getActivity().getSupportFragmentManager(); //Istanzio il fragment manager
        args.putInt("TIPOL", tipo); //Ci serve per sapere se ci serve o l'EditText o Lo Spinner
        args.putString("CAMPO", campo); //Campo da modificare
        args.putCharSequence("STRINGA", oldt1);
        DialogoModifica dialogo = new DialogoModifica(); //Istanzio l'oggetto della classe del Dialog
        dialogo.setArguments(args); //Passo gli argomenti
        dialogo.setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogFragment); //Setto uno stile personalizzato
        dialogo.show(fm, null); //Apro il Dialog
    }

Come dicevamo il DialogFragment è trattato come un fragment quindi passiamo dati allo stesso modo. Per settare lo stile usiamo il metodo setStyle() e per aprirlo chiamiamo show().
Vediamo il risultato di quanto abbiamo creato:

corso porgrammazione android lezione 10

Vi risparmio il layout essendo composto solo da componenti noti.

CoordinatorLayout

Il CoordinatorLayout è un layout particolare che ha come scopo la coordinazione dei componenti al suo interno.
Quindi avendo un layout con all’interno, ad esempio, un FloatingActionButton questo sale se un particolare evento richiama una SnackBar. Nel nostro caso abbiamo deciso di utilizzare l’animazione più eccitante (mi sono emozionato quando sono riuscito a farla per la prima volta, sembravo un bambino al lunapark), la Toolbar a scomparsa.

Corso Programmazione Android Lezione 10

Per farlo abbiamo dovuto inserire nel Layout dell’Activity il già citato NestedScrollView (ne nostro caso nel content_visualizza.xml). Possiamo notare che il FloatingActionButton resta fermo quando scorriamo

<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/paper_tile"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

Il responsabile dell’effetto è l’attributo layout_behavior. Nelle prossime lezioni vedremo i Behavior più usati.
Se siete curiosi consiglio lo studio dei sorgenti, ho aggiunto funzionalità che descriverò a breve.

Originariamente pubblicato su Be Geek My Friend

You May Have Missed