21 Mar 2011 Android – Navigazione TabActivity
Definizione della struttura principale
[codesyntax lang=”java” lines=”no” blockstate=”collapsed”]
package it.vivido.android.esempi; import it.vivido.android.esempi.testdue.TabActivityGroupUno; import it.vivido.android.esempi.testtre.TabActivityGroupDue; import it.vivido.android.esempi.testuno.TabActivityUno; import android.app.TabActivity;import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; import android.widget.TabHost; public class Esempio extends TabActivity{ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Resources res = getResources(); TabHost tabHost = getTabHost(); TabHost.TabSpec spec; Intent intent = new Intent().setClass(this, TabActivityUno.class); spec = tabHost.newTabSpec("TabActivity").setIndicator("Test Uno",res.getDrawable(R.drawable.home)).setContent(intent); tabHost.addTab(spec); intent = new Intent().setClass(this, TabActivityGroupUno.class); spec = tabHost.newTabSpec("TabActivityGroupUno").setIndicator("Test Due",res.getDrawable(R.drawable.compass)).setContent(intent); tabHost.addTab(spec); intent = new Intent().setClass(this, TabActivityGroupDue.class); spec = tabHost.newTabSpec("TabActivityGroupDue").setIndicator("Test Tre",res.getDrawable(R.drawable.compass)).setContent(intent); tabHost.addTab(spec); tabHost.setCurrentTab(0); } }
[/codesyntax]
Test Uno
In questo esempio l’apertura di un Activity sugli eventi dei tasti è la classica chiamata :
startActivity(edit);
E’ ovvio che il risultato è l’apertura dell Activity sull’intera maschera fuori dal contesto del TabActivity, questo esempio mi serve per sperimentare il funzionamento a pila della apertura e chiusura degli Activity. Questo comportamento è gestito in modo automatico in Android.
Come si può vedere dall’esempio via via che chiudo le varie maschere aperte si riapre quella chiamante.
[yframe url=’http://www.youtube.com/watch?v=Nhb_g05FUmM’]
[codesyntax lang=”java” lines=”no” strict=”no” blockstate=”collapsed”]
buttondue = (Button)findViewById(R.id.buttondue);
buttondue.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
Intent edit = new Intent(getApplicationContext(),TabActivityDue.class);
startActivity(edit);
}
});
[/codesyntax]
Quello che mi aspettavo, era un identico comportamento nella apertura di più activity all’interno del tab.
Test Due – Apertura degli activity nel contesto del Tab
Procediamo con un altro test, in tanto creiamo una classe che estende un ActivityGroup, che come descrive il link è un uno schermo che contiene e gestisce molteplici activity.
La funzione che vado a chiamare è getLocalActivityManager() che restituisce un LocalActivityManager , il cui scopo è di aiutare la gestione di multipli activity all’interno del ActivityGroup.
In particolare io utilizzo comunque la proprietà startActivity, che restituisce una Window utilizzata per assegnare alla view della ActivityGroup.
[codesyntax lang=”java” lines=”no” strict=”no” blockstate=”collapsed”]
Intent intent = new Intent(getParent(),TabActivityUno.class);
Window window = getLocalActivityManager().startActivity(“TabActivityUno”,intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
if (window != null)
{
setContentView(window.getDecorView());
}
[/codesyntax]
Mentre poi in tutti gli eventi dei vari pulsanti per l’apertura di un nuovo activity eseguo il seguente codice :
[codesyntax lang=”java” lines=”no” strict=”no” blockstate=”collapsed”]
TabActivityGroupUno padre =(TabActivityGroupUno)getParent();
Intent edit = new Intent(padre,TabActivityDue.class);
Window window =
padre.getLocalActivityManager().startActivity(“TabActivityDue”,
edit.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
if (window != null)
{
setContentView(window.getDecorView());
}
[/codesyntax]
L’unica differenza tra le due porzioni di codice è nella chiamata getParent che mi restituisce il contenitore padre che in questo caso è proprio ActivityGroup. Questo mi permette di aprire il secondo Activity sempre al suo interno.
Ma come si può vedere dal filmato successivo appena cerco di tornare al primo Activity aperto tramite un evento di back o una chiamata della funzione finish() mi si chiude definitivamente l’applicativo.
[yframe url=’http://www.youtube.com/watch?v=OcSW0SE0e9U’]
E’ interessante capire il perche, infatti gli eventi che andiamo a chiamare di backpressed non sono dell’activity visualizzato, ma ben si dell ActivityGroup che lo contiene. Quindi è ovvio che l’applicativo si chiuda in quanto ActivityGroup è effettivamente il primo aperto.
Test Tre – Corretta navigazione nei Tab
L’ultima prova che vi presento è in realtà una soluzione accettabile. Tutto si basa sulla seguente estensione della ActivityGroup.
[yframe url=’http://www.youtube.com/watch?v=vVAla6IPEbk’]
[codesyntax lang=”java” lines=”no” blockstate=”collapsed”]
.... private ArrayList<String> ListIdActivity; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (ListIdActivity == null) ListIdActivity = new ArrayList<String>(); } @Override public void finishFromChild(Activity child) { LocalActivityManager manager = getLocalActivityManager(); int index = ListIdActivity.size()-1; Log.i("finishFromChild ",ListIdActivity.get(index)); if (index < 1) { finish(); return; } manager.destroyActivity(ListIdActivity.get(index), true); ListIdActivity.remove(index); index--; String Id = ListIdActivity.get(index); Activity lastIntent = manager.getActivity(Id); Window newWindow =lastIntent.getWindow(); setContentView(newWindow.getDecorView()); } public void startChildActivity(String Id, Intent intent) { Window window = getLocalActivityManager().startActivity(Id,intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)); if (window != null) { ListIdActivity.add(Id); setContentView(window.getDecorView()); Log.i("Open Activity : ",Id); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { Log.i("onKeyUp ",""); onBackPressed(); return true; } return super.onKeyUp(keyCode, event); } @Override public void onBackPressed () { int length = ListIdActivity.size(); if ( length > 1) { Activity current = getLocalActivityManager().getActivity(ListIdActivity.get(length-1)); current.finish(); } }
[/codesyntax]
In particolare sfruttando una lista di Stringhe si va a ricreare l’effettuto della gestione a pila di Android a mano. Come potete vedere nell’evento di finishFromChild viene recuperato l’activity precedentemente aperto per poi rivisualizzarlo, evitando di riaprire sempre un nuovo oggetto.
Dopo di che basta estendere questa classe invece dell’ ActivityGroup e il risultato è accettabile.
Abbiamo ancora un problema sempre legato alla gestione del landscape. Appena ruotiamo il cellulare si può notare che viene perso il riferimento all’ activity aperto. Io attualmente ho risolto solo bloccando la possibilità di ruotare l’applicativo.
Matteo
Posted at 19:08h, 15 GennaioCiao,sarebbe possibile avere il codice completo di questa app di prova ?
Andrea Serena
Posted at 09:02h, 16 GennaioCiao Matteo
puoi trovare a questo link il codice completo, http://devteam.vivido.it/wp-content/uploads/EsempioTab.zip.
Saluti
Andrea
Matteo
Posted at 21:21h, 25 GennaioGrazie mille !!!
Alberto
Posted at 08:58h, 17 MaggioCiao, stavo studiando questa maledetta tabactivity e mi sono imbattuto in questo vostro “tutorial”.
Volevo chiedere se avete idee in merito a quanto mi accade:
Io mi trovo nella condizione di aver alimentato 4 tabs con icone personalizzate ecc ma il contenuto del primo tab, che contiene una sola activity e che dovrebbe visualizzare uno specifico layout, non viene visualizzato correttamente (gli altri tab vengono visualizzati correttamente). Il layout in questione contiene una expandablelistview ed un pulsante … Avete idee?
Volevo poi segnalare che nel vostro tutorial, nel tab “Test Tre” non viene gestita la pressione del tasto chiudi, mi permetto di integrare con questa porzione di codice dopo aver aggiunto il pulsante [buttonchiudi] fra le altre variabili:
buttonchiudi = (Button)findViewById(R.id.chiudi);
buttonchiudi.setVisibility(View.VISIBLE); // anche se inutile
buttonchiudi.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
onBackPressed(); // oppure finish();
}
});
Andrea Serena
Posted at 06:15h, 18 MaggioCiao Alberto
francamente cosi su due piedi non saprei, non mi è mai capitato nulla di simile.
Grazie per l’integrazione.
Andrea