mercoledì 13 luglio 2011

Menu con le ViewStub

Prima di tutto creiamo un nuovo progetto con Eclipse.
Il Manifest non subirà cambiamenti particolari quindi tralasciamo la sua visualizzazione e analizziamo invece il layout dell’Activity principale.

Sorgenti:
stubs.xml
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">

<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="vertical"
android:gravity="center">
<EditText android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Click...but do nothing" />
</LinearLayout>

<ViewStub android:id="@+id/stub_menu" android:inflatedId="@+id/panel_menu"
android:layout="@layout/menu" android:layout_width="fill_parent"
android:layout_height="fill_parent" android:layout_gravity="top" />

</merge>

La prima cosa che salta all’occhio direi è l’utilizzo del tag <merge>
Cos’è ? A cosa serve ? Il tag <merge> è stato creato per ridurre il numero di livelli nella gerarchia delle Views, sembra un po’ ostica da capire come cosa, ma vedendolo in azione si intuisce meglio il suo scopo. In altre parole, basta pensare che ogni View, e quindi ogni Layout, è figlio di un’altra View (o Layout) e difatti quando si crea un XML layout bisogna specifare un root Layout, che verrà istanziato poi al momento di generare il codice Java corrispondente dalla classe LayoutInflater. In questo caso però, quando il LayoutInflater incontrerà il tag <merge> non creerà una nuova istanza di un Layout ma non farà altro che unire la nostra View alla top-level View, cioè alla View di alto livello già istanziata. Comunque per altri chiarimenti vi rimando alla bibliografia in fondo al tutorial. Un altro motivo per cui ho utilizzato il tag <merge> è per poter sovrapporre le due View del layout, che sono il LinearLayout e, per l’appunto, la nostra ViewStub !
Il LinearLayout comprende una EditText e un Button, che ho messo a puro scopo indicativo, mentre vorrei soffermarmi un po’ sulla ViewStub, anche perché è il motivo di questo tutorial !

Analizzando la ViewStub

La ViewStub è una View che non ha dimensioni, non viene disegnata e che quindi non viene considerata in nessun modo all’interno del nostro layout. In pratica l’unica cosa che facciamo è dichiarare la sua esistenza per poterla poi usare in seguito quando ne avremo bisogno ! Il beneficio di usare una ViewStub è quello di non appesantire il sistema nella creazione di nuovi elementi a runtime, un po’ come la tecnica del lazy loading delle immagini,che spopola nel progettazione Web 2.0

Gli attributi della ViewStub

Quello che ci serve per dichiarare una ViewStub è un android:id, cioè l’identificativo della ViewStub stessa, e un android:layout, che non è altro che il riferimento al file XML contenente il layout che andrà a “popolare” la ViewStub. Un terzo attributo è l’android:inflateId, cioè l’identificativo del file XML contenente il layout (quello specificato prima). Per il resto, larghezza e altezza ecc.. sono i classici attributi che ritroviamo nella creazione dei layout. Nel caso specifico, ho settato come android:layout_gravity il valore top, in modo da posizionare la ViewStub in alto nello schermo.
Ok direi che per ora può bastare, vedremo successivamente come gestire la ViewStub all’interno dell’Activity. Per ulteriori informazioni vi rimando alla bibliografia in fondo a questo tutorial.

Il layout del menu

Quello di seguito altro non è che il layout XML del menu vero e proprio, quello che sarà dichiarato nell’attributo android:layout della ViewStub

Sorgenti:
menu.xml
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:background="#99000000">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content" android:padding="20dp"
android:orientation="horizontal" android:gravity="center"
android:background="#888">
<Button android:id="@+id/button_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Button 1" />
<Button android:id="@+id/button_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Button 2" />
<Button android:id="@+id/button_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Button 3" />
</LinearLayout>
</LinearLayout>

Molto semplice, due LinearLayout, uno dentro l’altro. Il secondo è quello che contiene i 3 pulsanti di esempio, mentre il primo è il nostro root Layout !
Ed ecco il trick . Nel primo LinearLayout ho settato come attributo android:background il valore “#99000000” , il quale darà il nostro tocco di stile al menu, rendendo lo sfondo trasparente (o traslucente). Ma cosa sono quei numeri ? Non è altro che il riferimento ad un colore del tipo rgb con una componente alpha-transparency, in pratica i primi due numeri -99- definiscono la alpha-transparency (a), i seguenti 2 numeri -00- la componente red (r), i successivi -00- il green (g) e infine gli ultimi due -00- il blue (b). Tradotto, ho settato lo sfondo del LinearLayout con il colore nero e un grado di trasparenza pari a 99. Provate a variare i primi due numeri (da 00 a 99) per vedere le varie differenze di trasparenza.

L’Activity principale

Ed eccoci finalmente al codice dell’Activity principale (nonché l’unica del tutorial).

Sorgenti:
StubActivity.java
Codice (Java): [Seleziona]
public class StubActivity extends Activity {
/** Called when the activity is first created. */
private View menu;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.stubs);
menuInit();
}

private void menuInit() {
menu = ((ViewStub) findViewById(R.id.stub_menu)).inflate();
menu.setVisibility(View.GONE);
Button b1 = (Button) menu.findViewById(R.id.button_1);
b1.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(),
"Click on Button 1",
Toast.LENGTH_SHORT).show();
}

});
Button b2 = (Button) menu.findViewById(R.id.button_2);
b2.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(),
"Click on Button 2",
Toast.LENGTH_SHORT).show();
}

});
Button b3 = (Button) menu.findViewById(R.id.button_3);
b3.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View arg0) {
Toast.makeText(getApplicationContext(),
"Click on Button 3",
Toast.LENGTH_SHORT).show();
}

});
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_MENU:
int visibility = menu.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE;
menu.setVisibility(visibility);
break;
case KeyEvent.KEYCODE_BACK:
if (menu.getVisibility() == View.VISIBLE) {
menu.setVisibility(View.GONE);
} else
this.finish();
break;
default:
break;
}
return false;
}

}

Come sempre, creiamo una classe che estende Activity e settiamo la View principale con il file stubs.xml, dopodiché inizializziamo il menu.
Ho creato il metodo menuInit() proprio per fare questo, vediamo cosa fa.

Codice (Java): [Seleziona]
menu = ((ViewStub) findViewById(R.id.stub_menu)).inflate();
Ecco il tassello mancante per l’utilizzo delle ViewStub. Il metodo inflate() inserisce di fatto la ViewStub all’interno del nostro layout, restituendo il riferimento alla View che contiene il layout del menu. Da questo momento la View diventa visibile a tutti gli effetti, ecco perché con la successiva riga di codice

Codice (Java): [Seleziona]
menu.setVisibility(View.GONE);
Rimuovo la View da flusso delle View visibili a schermo, perché per il momento il menu non deve essere visibile.
Ora che abbiamo il riferimento alla View del menu, possiamo interagire con i componenti che la popolano. Nello specifico, è stato assegnato un OnClickListener ai 3 pulsanti del menu, i quali al loro click faranno apparire a video un messaggio.

Gestire il menu sull’onKeyDown

Ora che il menu è inizializzato possiamo mostrarlo e renderlo fruibile. In questo tutorial ho deciso di farlo apparire alla pressione del tasto menu del device (ma guarda il caso… ) ma nulla vieta di gestirlo diversamente. Il codice non è ottimizzato al massimo, ma d’altronde è solo un esempio.
Con queste righe di codice

Codice (Java): [Seleziona]
int visibility = menu.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE;
menu.setVisibility(visibility);

Rendo visibile o meno il menu alla pressione del tasto menu.
Dato che stiamo facendo l’override del metodo onKeyDown dell’Activity, il tasto “indietro” del device sarà ora inutilizzabile, quindi andiamo a gestire anche questo, per poter permettere l’uscita dall’applicazione nel modo tradizionale, e già che ci siamo aggiungiamo un altro tocco di stile al nostro menu, nascondendolo anche alla pressione del suddetto tasto

Codice (Java): [Seleziona]
if (menu.getVisibility() == View.VISIBLE) {
menu.setVisibility(View.GONE);
} else this.finish();

E con questo è tutto, abbiamo realizzato un menu con sfondo trasparente con il solo ausilio delle ViewStub. In questo modo l’app risulta notevolmente meno appesantita, il nostro codice è modulare e la nostra ViewStub è riusabile per altri progetti.

Bibliografia: