Corso di Programmazione Android Lezione 8: Miglioriamo l’aspetto della nostra App

Corso di Programmazione Android Lezione 8

Corso di Programmazione Android Lezione 8: Miglioriamo l’aspetto della nostra App

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

In questa lezione del corso cercheremo di rendere più gradevole l’aspetto della nostra applicazione.
Il codice sorgente mostrato in questa lezione è reperibile qui.

Pratica

I drawable

Per prima cosa creiamo un Drawable da utilizzare come sfondo di alcuni componenti. La documentazione ufficiale da la seguente definizione di risorsa Drawable:

Una risorsa Drawable è un concetto generale di componente grafico che può essere disegnata sullo schermo e che si può recuperare con API o applicare a un’altra risorsa XML.

Ci sono molti tipi di risorse Drawable (tutti elencati nella documentazione ufficiale) noi creeremo un semplice rettangolo con sfondo bianco trasparente e bordi arrotondati. Ecco il file drawable/border_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">
  <padding
    android:left="15dp"
    android:top="4dp"
    android:right="15dp"
    android:bottom="4dp"/>
  <stroke
    android:width="1dp"
    android:color="#D8FDFB"/>
  <corners android:radius="5dp"/>
  <solid android:color="#90FFFFFF"/>
</shape>

Creiamo una forma (shape) di tipo rettangolo (rectangle), il contenuto sarà distante dal bordo (padding) per i dp specificati, avremo un bordo (stroke) di 1 dp del colore specificato (un tonalità di grigio), degli angoli (corners) arrotondati e all’interno (solid) avremo un colore bianco con trasparenza del 90%.
Nella cartella Drawable ho anche inserito un’immagine che useremo come sfondo dei fragment di tutte le activity (attenzione le activity aggiungi…. sono da considerarsi come hanno uno stile Dialog quindi le lasciamo con sfondo scuro).

 Il nuovo layout del Fragment di Visualizzazione

Dobbiamo modificare in modo massiccio il nostro layout di visualizzazione quindi ci conviene staccare la parte più corposa in un file separato, anche perché può essere utile riutilizzare uno o tutti e due i file.
Ecco il nuovo layout del fragment di visualizzazione (layout/fragment_vis_tab1.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">

    <include layout="@layout/content_visualizza" />

    <android.support.design.widget.FloatingActionButton
    android:id="@+id/fb_delete"
        android:src="@mipmap/ic_action_discard"
    app:fabSize="normal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|right"
    android:layout_marginBottom="65dp"
        android:layout_marginEnd="10dp" />

</android.support.design.widget.CoordinatorLayout>

Il nuovo content_visualizza.xml

<?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="match_parent"
    android:background="@drawable/paper_tile"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_marginRight="5dp"
            android:layout_weight="1">
            <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>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_weight="1">
            <TextView
                android:id="@+id/autore_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/autore"/>
            <TextView
                android:id="@+id/autore_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginBottom="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_marginRight="5dp"
            android:layout_weight="1">
            <TextView
                android:id="@+id/formato_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/formato"/>
            <TextView
                android:id="@+id/formato_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_weight="1">
            <TextView
                android:id="@+id/genere_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/genere"/>
            <TextView
                android:id="@+id/genere_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            </LinearLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginBottom="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_marginRight="5dp"
            android:layout_weight="1">
            <TextView
                android:id="@+id/posizione_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/posizione"/>
            <TextView
                android:id="@+id/posizione_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_weight="1">
            <TextView
                android:id="@+id/prestato_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/prestato"/>
            <TextView
                android:id="@+id/prestato_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginBottom="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_marginRight="5dp"
            android:layout_weight="1">
            <TextView
                android:id="@+id/prestato_chi_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/prestato_chi"/>
            <TextView
                android:id="@+id/prestato_chi_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_weight="1">
            <TextView
                android:id="@+id/prestato_quando_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/prestato_quando"/>
            <TextView
                android:id="@+id/prestato_quando_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

Il layout sembra complesso, ma osservandolo attentamente notiamo che abbiamo fatto praticamente una tabella.
Con l’attributo android:background=”@drawable/paper_tile” associamo al layout “padre” il nostro drawable di sfondo.
Analizziamo una parte del layout nello specifico una riga della tabella:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_marginRight="5dp"
            android:layout_weight="1">
            <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>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_weight="1">
            <TextView
                android:id="@+id/autore_label"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                android:text="@string/autore"/>
            <TextView
                android:id="@+id/autore_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>

Abbiamo definito un contenitore con orientamento orizzontale cha a sua volta conterrà due contenitori figli (le colonne) con orientamento verticale, vediamone uno:

<LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical"
            android:background="@drawable/border_layout"
            android:layout_marginRight="5dp"
            android:layout_weight="1">
            <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>

L’unico attributo che non conosciamo è android:weight.
Impostando lo stesso valore per tutti i figli del contenitore padre (nel nostro caso il layout con orientamento orizzontale) e impostando uno degli attributi layout_width o layout_height  a match_parent (utilizza tutto lo spazio) i due componenti si divideranno lo spazio in parti uguali.  Essendo il nostro contenitore padre di orientamento orizzontale a noi serve impostare layout_width.
Con l’attributo android:background in questo caso associamo il nostro rettangolo trasparente (lo sfondo è troppo scuro, quindi serve un pezzetto di vetro sopra)

Miglioriamo l’aspetto anche della ListView

L’aspetto di default della nostra ListView (libri_fragment), non è brutto come era l’activity di visualizzazione ma possiamo migliorarlo. Per farlo creiamo un layout personalizzato per il componente (file layout/cursor_layout.xml)

<?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="vertical"
    android:background="@drawable/border_layout"
    android:layout_marginTop="5dp">
    <TextView
        android:id="@+id/raw1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_gravity="center"/>
    <TextView
        android:id="@+id/raw2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_gravity="center"/>
</LinearLayout>

Anche qui mettiamo come sfondo il nostro quadratino di vetro. Applichiamo il nuovo layout:

cur = new SimpleCursorAdapter(
            getActivity(),
      R.layout.cursor_layout,
      c,
      new String[] {DatabaseBeReader.BeReaderMetaData.TITOLO, DatabaseBeReader.BeReaderMetaData.AUTORE},
      new int[]{R.id.raw1 , R.id.raw2},
      0);
      libriLV.setAdapter(cur);

Ricordiamoci di applicare il nuovo layout anche all’interno del metodo onResume().

Gli stili

Quando vogliamo definire l’aspetto di uno o più componenti, Activity e/o dell’intera applicazione possiamo affidarci a gli stili. Possiamo pensare a gli stili come a emuli dei CSS.  Per la nostra applicazione necessitiamo di due file di stile: uno generico  e uno per device equipaggiati con Android 5.0 o superiore (Sdk v21). Ecco il generico styles.xml posizionato nella cartella values/

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
         <item name="android:textSize">18sp</item>
    </style>

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />

    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />


</resources>

Qui definiamo lo stile principale (comune a tutte le architetture) e un sotto stile (ottenuto usando la notazione NomeTema.NomeSottoTema) che semplicemente si occupa togliere l’Actionbar per far posto alla Toolbar. Ho lasciato tutto come da default aggiungendo solo aggiunto il parametro android:textSize (i nostri occhi si meritano un carattere più grande).
Il file specifico per l’Sdk v21 ridefinisce il sottostile aggiungendo parametri ulteriori (utilizzabili solo per Android 5.0 o superiori), il file si trova nella cartella values-v21 e come il precedente su chiama styles.xml

<resources>>

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@color/colorPrimaryDark</item>
  </style>

</resources>

 Risultato

Prima

corso programmazione android lezione 8

Dopo

corso programmazione android lezione 8

Molto meglio no 🙂

Piccole correzioni alla classe VisualizzaLibri

I più attenti avranno notato un piccolo bug relativo al tasto back della Toolbar, infatti premendolo non torniamo, alla giusta scelta di menu ma alla homepage (per ora vuotissima). Il problema viene dal fatto che, se non implementato diversamente, alla pressione del tasto back si torna si all’actvity specificata nel file AndroidManifest.xml ma riavviandola completamente ( stato salvato azzerato). Catturando la pressione del tasto (come se fosse una voce di menu) e forzando un finish sulla nostra Activity possiamo risolvere.

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            // Respond to the action bar's Up/Home button
            case android.R.id.home:
                //NavUtils.navigateUpFromSameTask(this);
                finish();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

You May Have Missed