Autore Topic: [Facile]Android Design - Action Bar  (Letto 34139 volte)

Zate

  • Visitatore
[Facile]Android Design - Action Bar
« il: 28 Gennaio 2012, 16:38:30 CET »
+17
Livello di difficoltà: facile
Target SDK: 15
Link al progetto: in allegato

Una tra le più evidenti novità di Android è l'Action Bar. Fino ad Honeycomb era un'esclusiva dei tablet, ma con l'avvento di Ice Cream Sandwich è destinata a diffondersi. Sul blog degli sviluppatori hanno anche scritto un post dicendo che il tasto menu sarà sostituito dall'action bar. L'autore del post fornisce anche un link a delle icone "standard". Vi consiglio di leggerlo. Potete comunque creare le vostre icone, seguendo queste istruzioni
Implementare l'Action Bar potrebbe sembrare difficile, ma non è assolutamente vero. Penso che ne valga la pena, in pochi giorni si può rendere la grafica molto più raffinata con poche righe di codice.

Prima di cominciare a scrivere codice c'è bisogno di decidere come orientarsi sulla grafica. Esistono tre temi in ICS:
_Holo Dark (Download, Impostazioni ecc.)
_Holo Light(Calendario, Gmail ecc.)
_Holo Light con action bar dark(Messaggi ecc.)

Per scegliere quale tema usare basta aggiungere una riga di codice nel tag application o activity:
Per Holo Dark:
Codice (XML): [Seleziona]
android:theme="@android:style/Theme.Holo"Per Holo Light:
Codice (XML): [Seleziona]
android:theme="@android:style/Theme.Holo.Light"Per Holo Light con Action Bar Dark:
Codice (XML): [Seleziona]
android:theme="@android:style/Theme.Holo.Light.DarkActionBar"Potete comunque personalizzare a vostro piacimento i temi.

Una volta scelto il tema che più si addice alla vostra idea si può cominciare a mettere le fondamenta del progetto.
Questo è il mio manifest, se seguite alla lettera il tutorial andrà bene e non dovrete ricordarvi di dichiarare ogni activity. Ricordatevi che l'Action Bar è disponibile dall'API Level 11 in poi. Per la compatibilità pre-Honeycomb vedere qui
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.design.zate"
   android:versionCode="1"
   android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="11"
       android:targetSdkVersion="15"/>

    <application
       android:icon="@drawable/ic_launcher"
       android:label="@string/app_name"
       android:hardwareAccelerated="true">
        <activity
           android:name=".HomeActivity"
           android:label="HoloDark"
           android:theme="@android:style/Theme.Holo">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
           android:name=".ICSDesignActivity"
           android:label="SplitAB"
           android:theme="@android:style/Theme.Holo.Light"
           android:uiOptions="splitActionBarWhenNarrow">
            <intent-filter>
                <action android:name="android.intent.action.ICSDESIGN" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
           android:name=".SearchActivity"
           android:label="Search"
           android:theme="@android:style/Theme.Holo">
        </activity>
        <activity
           android:name=".ActionProviderActivity"
           android:label="Share"
           android:theme="@android:style/Theme.Holo.Light">
        </activity>
        <activity
           android:name=".TabsActivity"
           android:label="Tabs"
           android:theme="@android:style/Theme.Holo.Light.DarkActionBar">
        </activity>
        <activity
           android:name=".DropDownListActivity"
           android:label="DropDownList"
           android:theme="@android:style/Theme.Holo">
        </activity>
    </application>

</manifest>

Poi ho scritto un semplice layout con una TextView e un Button (in modo da vedere le differenze tra i vari temi).
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:orientation="vertical" >

    <TextView
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:text="@string/hello" />

    <Button
       android:id="@+id/button1"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Button" />

</LinearLayout>

Per inserire dei comandi nell'ActionBar bisogna creare un xml in res/menu. Io il mio l'ho chiamato main_activity_holo_dark_action_bar (un nome facile da ricordare ;) ). Anche qui si tratta di poche righe di codice:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
     <item
       android:id="@+id/abForwardHoloDark"
       android:icon="@drawable/holo_dark_forward"
       android:title="Forward"
       android:showAsAction="ifRoom|withText"/>
</menu>
Cosa significa tutto questo? android:id non c'è bisogno di spiegarlo, icon è l'icona che io ho chiamato holo_dark_forward e, anche se è opzionale, è preferibile metterla (ho avuto un po' di problemi perché eclipse non riusciva a trovare l'immagine, se anche voi dovreste avere questi problemi provate a fare un refresh o un clean, se non funziona riavviate eclipse. Se non funziona ancora c'è qualcosa di sbagliato), title è il titolo, è importante dichiararlo perché per i non vedenti verrà letto il titolo, perché se apparirà solo l'icona, l'utente, tenendola premuta, potrà vedere il titolo e perché se non c'è abbastanza spazio nell'ActionBar, l'item apparirà nel menu overflow. ifRoom sta infatti ad indicare che apparirà nell'ActionBar solo se c'è spazio, withText indica invece che, se c'è spazio, apparirà anche il testo, ovvero il titolo (provate a girare in landscape mode). Potete anche aggiungere più elementi nell'action bar semplicemente aggiungendo tag item. Ricordatevi che se non ci sarà abbastanza spazio appariranno nel menu overflow.

Adesso tocca al Java. Anche qui l'implementazione è davvero semplice e bastano poche righe di codice. Qui c'è la mia classe HomeActivity:
Codice (Java): [Seleziona]
package com.design.zate;

import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

public class HomeActivity extends Activity{

        @Override
        protected void onCreate(Bundle savedInstanceState) {
                // TODO Auto-generated method stub
                super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ActionBar ab = getActionBar();
        ab.setHomeButtonEnabled(true);
        }
       
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
                // TODO Auto-generated method stub
                MenuInflater mMenuInflater = getMenuInflater();
                mMenuInflater.inflate(R.menu.main_activity_holo_dark_action_bar, menu);
                return true;
        }
       
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case R.id.abForwardHoloDark:
                        Intent intent = new Intent(this, ICSDesignActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent);
                        return true;
                default:
                    return super.onOptionsItemSelected(item);
            }
        }

}
Cerco di spiegare cosa ho fatto brevemente e chiaramente.
Per poter utilizzare le api dell'action bar bisogna prima di tutto "prenderla". Per fare ciò si chiama getActionBar().
setHomeButtonEnabled consentirà di premere l'icona in alto a sinistra chiamando il metodo onOptionsItemSelected() e con l'id android.R.id.home. In questa activity non serve, ma secondo me è importante che l'utente possa fare e schiacciare un po' di tutto (o magari è solo una cavolata  :-P). Ricordatevi di questo perché più avanti sarà importante.
Quando l'activity parte, viene chiamato il metodo onCreateOptionsMenu() e l'action bar viene popolata grazie al MenuInflater. Se vi ricordate main_activity_holo_dark_action_bar è il nome che avevo dato al file dove avevo scritto i comandi dell'action bar.
Quando viene premuto un tasto nell'action bar viene chiamato il metodo onOptionsItemSelected() qui con uno switch decidiamo che se l'id ricevuto è abForwardHoloDark (deciso in main_activity_holo_dark_action_bar) faremo partire l'activity ICSDesignActivity. Per ora non preoccupatevi di intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), per adesso non noterete nessuna differenza, ma dopo diventerà importante.
Provate la vostra activity adesso, tutto dovrebbe funzionare (tranne ovviamente il tasto forward, dato che non abbiamo ancora creato l'activity ICSDesignActivity).



La seconda activity che ho creato è appunto ICSDesignActivity. In questa activity ho usato il tema Holo Light e, se ci avete fatto caso, ho aggiunto android:uiOptions="splitActionBarWhenNarrow". Significa che i comandi verranno aggiunti nella parte inferiore, in questo modo.
Il file xml dell'action bar sarà simile a quello di prima. L'ho chiamato main_activity_holo_light_action_bar.
Eccolo qui:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
       android:id="@+id/abForwardHoloLight"
       android:icon="@drawable/holo_light_forward"
       android:title="Forward"
       android:showAsAction="ifRoom|withText"/>
</menu>
Non penso che ci sia qualcosa da spiegare in quanto non c'è niente di nuovo rispetto a quello usato prima.
La vera differenza sta nell'activity. Eccola qui:
Codice (Java): [Seleziona]
package com.design.zate;

import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

public class ICSDesignActivity extends Activity {

        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                ActionBar ab = getActionBar();
                ab.setHomeButtonEnabled(true);
                ab.setDisplayHomeAsUpEnabled(true);
        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
                // TODO Auto-generated method stub
                MenuInflater mMenuInflater = getMenuInflater();
                mMenuInflater.inflate(R.menu.main_activity_holo_light_action_bar, menu);
               
                return true;
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
                switch (item.getItemId()) {
                case android.R.id.home:
                        Intent intent = new Intent(this, HomeActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent);
                        return true;
                case R.id.abForwardHoloLight:
                        Intent intent2 = new Intent(this, SearchActivity.class);
                        intent2.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent2);
                        return true;
                default:
                        return super.onOptionsItemSelected(item);

                }
        }
}
Come prima ho reso l'icona cliccabile, ma questa volta ho aggiunto setDisplayHomeAsUpEnabled(true). Accanto all'icona verrà aggiunta una piccola freccetta che introduce un concetto parzialmente nuovo.
Come ho scritto prima, cliccando l'icona verrà chiamato il metodo onOptionsItemSelected() con l'id android.R.id.home. Come vedete questa volta ho aggiunto questo id nello switch. Nel suo case lancio l'activity HomeActivity, quella precedente. Qui entra in gioco FLAG_CLEAR_ACTIVITY_TOP. Per capire meglio:
Senza FLAG_CLEAR_ACTIVITY_TOP:
ActivityA > ActivityB > SU > ActivityA > BACK > ActivityB
Con FLAG_CLEAR_ACTIVITY_TOP:
ActivityA > ActivityB > SU > ActivityA > BACK > Fine
Si aggiunge su, che è simile a back, ma ci sono differenze. Per capire bene è forse molto più semplice leggere (o guardare le immagini, si capisce lo stesso) di questa pagina o provare con la pratica invece che con la teoria.
In questa activity non ci sono altre novità rispetto alla prima, solo il tasto su e i comandi in basso.


La terza activity è simile alle altre due, ma ha una novità: l'aggiunta di una action view nell'action bar.
L'action bar nell'xml (action_view nel mio caso) diventerà:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
       android:id="@+id/abSearch"
       android:actionViewClass="android.widget.SearchView"
       android:icon="@drawable/holo_dark_search"
       android:showAsAction="ifRoom|collapseActionView"  
       android:title="Search"/>
    <item
       android:id="@+id/abForwardHoloDark"
       android:icon="@drawable/holo_dark_forward"
       android:showAsAction="ifRoom|withText"
       android:title="Forward"/>

</menu>
Utilizzo una SearchView. Avendo aggiunto android:showAsAction="ifRoom|collapseActionView"  quando si toccherà il comando search, gli altri comandi dell'action bar finiranno nel menu overflow. Un esempio è l'app di YouTube (sempre con il comando cerca). Si capisce meglio con la pratica comunque. Il tasto forward avrà la stessa funzione delle due activity precedenti, andare avanti.
Questa è la classe SearchActivity:
Codice (Java): [Seleziona]
package com.design.zate;

import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.TextView;
import android.widget.Toast;

public class SearchActivity extends Activity implements OnQueryTextListener {

        TextView tv;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
                // TODO Auto-generated method stub
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);

                ActionBar ab = getActionBar();
                ab.setHomeButtonEnabled(true);
                ab.setDisplayHomeAsUpEnabled(true);

                tv = (TextView) findViewById(R.id.textView1);

        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
                // TODO Auto-generated method stub
                MenuInflater mMenuInflater = getMenuInflater();
                mMenuInflater.inflate(R.menu.action_view, menu);
                SearchView mSearchView = (SearchView) menu.findItem(R.id.abSearch)
                                .getActionView();
                mSearchView.setOnQueryTextListener(this);
                return true;
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
                switch (item.getItemId()) {
                case android.R.id.home:
                        Intent intent = new Intent(this, ICSDesignActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent);
                        return true;
                case R.id.abForwardHoloDark:
                        Intent intent2 = new Intent(this, ActionProviderActivity.class);
                        intent2.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent2);
                        return true;
                default:
                        return super.onOptionsItemSelected(item);
                }
        }

        @Override
        public boolean onQueryTextChange(String newText) {
                // TODO Auto-generated method stub
        newText = newText.isEmpty() ? "" : "Query: " + newText;
                tv.setText(newText);
                return true;
        }

        @Override
        public boolean onQueryTextSubmit(String query) {
                // TODO Auto-generated method stub
        Toast.makeText(this, "Searching for " + query + "...", Toast.LENGTH_SHORT).show();
                return true;
        }

}
Non cercherà niente, è solo un modo per spiegare come utilizzare le action view nell'action bar. Se volete creare un'interfaccia per cercare qualcosa, andate qui.
La novità sta nel fatto che il comando search si allargherà "rubando" spazio agli altri comandi. Nella SearchView usiamo l'id scritto nell'xml dell'action bar in action_view, abSearch. Quando cambia il testo nella textview verrà scritto Query: + il testo che si vuole cercare. Quando si schiaccia il pulsante cerca nella tastiera apparirà un toast con scritto cosa stiamo cercando. Non mi sono soffermato molto su come creare l'interfaccia per cercare perché non è questo il punto del tutorial ;)


Un altro comando in cui vi sarete sicuramente imbattuti è quello "Condividi". Si può implementare aggiungendo un action provider.
Nell'xml dell'action bar che io ho chiamato action_provider
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
       android:id="@+id/abShare"
       android:actionProviderClass="android.widget.ShareActionProvider"
       android:icon="@drawable/holo_light_share"
       android:showAsAction="ifRoom"
       android:title="Share"/>
    <item
       android:id="@+id/abForwardHoloLight"
       android:icon="@drawable/holo_light_forward"
       android:showAsAction="ifRoom|withText"
       android:title="Forward"/>

</menu>
Niente di particolare, al posto di android:actionViewClass="android.widget.SearchView" si usa android:actionProviderClass="android.widget.ShareActionProvider". ShareActionProvider è un'estensione dell'ActionProvider che facilita la condivisione mostrando una lista di applicazioni che maneggiano l' ACTION_SEND. Aprendo un'immagine nella galleria e toccando il comando condividi apparirà una lista di applicazioni con le quali potete condividere l'immagine: Picasa, Messaggi, Facebook, Bluetooth, Gmail ecc. Di default le applicazioni sono ordinate in base a quante volte l'utente sceglie l'applicazione. Inoltre si può scegliere quale icona far apparire accanto al tasto condividi tenendola premuta.
Anche qui non mi soffermerò particolarmente su come condividere realmente, mi concentrerò su come "inizializzare" la condivisione.
Questo è il codice dell'activity ActionProviderActivity:
Codice (Java): [Seleziona]
package com.design.zate;

import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ShareActionProvider;

public class ActionProviderActivity extends Activity{
       
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                // TODO Auto-generated method stub
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
                ActionBar ab = getActionBar();
                ab.setHomeButtonEnabled(true);
                ab.setDisplayHomeAsUpEnabled(true);
        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
                // TODO Auto-generated method stub
                MenuInflater mMenuInflater = getMenuInflater();
                mMenuInflater.inflate(R.menu.action_provider, menu);
                ShareActionProvider mShareActionProvider =(ShareActionProvider) menu.findItem(R.id.abShare).getActionProvider();
                mShareActionProvider.setShareIntent(createShareIntent());
                return true;
        }


        private Intent createShareIntent() {
                // TODO Auto-generated method stub
                Intent mShareIntent = new Intent();
                mShareIntent.setType("image/*");
                return mShareIntent;
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
                // TODO Auto-generated method stub
                switch(item.getItemId()){
                case android.R.id.home:
                        Intent intent = new Intent(this, SearchActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent);
                        return true;
                case R.id.abShare:
               
                case R.id.abForwardHoloLight:
                        Intent intent2 = new Intent(this, TabsActivity.class);
                        intent2.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent2);
                        return true;
                default:
                        return super.onOptionsItemSelected(item);
                }
        }

}
Qui creiamo uno ShareActionProvider prendendo quello nell'xml, abShare.
A questo ShareActionProvider dobbiamo assegnare un intent, lo facciamo creando il metodo createShareIntent(). In questo metodo creiamo un intent, gli assegnamo un tipo di risorsa (ma potreste mettere anche text, per esempio) e poi lo ritorniamo. In questo modo, cliccando Share dovrebbe comparire una lista di applicazioni che supportano il tipo di risorsa image. Provare per credere ;) Ovviamente se selezionassimo un applicazione riceveremmo degli errori in quanto non abbiamo inviato nessuna immagine ma solo l'intent.


Adesso comincia una parte un po' più complicata, ma comunque non è niente di impossibile. Le tabs. Per capire meglio di cosa sto parlando basta aprire l'applicazione del telefono, dei contatti o di YouTube. Per poter capire bene questa parte è bene leggersi un po' cosa sono i fragment (che vi saranno utili anche al di fuori di questo tutorial, quindi vi consiglio vivamente di dare almeno una lettura veloce alla pagina linkata).

Dopo questa premessa/avvertimento/minaccia possiamo cominciare  :-P
Per l'importanza di queste tabs ho deciso di creare anche un layout diverso, in modo da cercare di spiegare più cose. Anche questo non è un layout particolare (io l'ho chiamato tabs.xml), eccolo qui:
Codice (XML): [Seleziona]
<?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:orientation="vertical" >

    <FrameLayout
       android:id="@+id/flFragmentContent"
       android:layout_width="match_parent"
       android:layout_height="100dp" />

    <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical" >

        <Button
           android:id="@+id/bShowActionBar"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="Show Action Bar" />

        <Button
           android:id="@+id/bHideActionBar"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="Hide Action Bar" />

        <Button
           android:id="@+id/bAdd"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="Add" />

        <Button
           android:id="@+id/bRemove"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="Remove" />

        <Button
           android:id="@+id/bRemoveAll"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="Remove all" />
    </LinearLayout>

</LinearLayout>
Il FrameLayout (attenzione che non ha children) sarà il contenuto dei fragment che andremo a creare. Se avete letto il layout vi sarete sicuramente accorti di un Button per creare tabs, in ogni tab aggiungeremo una textview. Ho creato un xml chiamato tabscontent, eccolo qui:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/tvTabText"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content" />
Questa volta non creeremo nessun xml per l'action bar, useremo quello già creato prima: main_activity_holo_dark_action_bar
Ho creato la classe TabsActivity, questo è il codice:
Codice (Java): [Seleziona]
package com.design.zate;

import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class TabsActivity extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.tabs);

                final ActionBar ab = getActionBar();
                ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

                ab.setHomeButtonEnabled(true);
                ab.setDisplayHomeAsUpEnabled(true);

                ab.addTab(ab.newTab().setText("First Tab")
                                .setTabListener(new TabListener(new TabContentFragment())));

                Button bShow = (Button) findViewById(R.id.bShowActionBar);
                bShow.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View v) {
                                // TODO Auto-generated method stub
                                if (!ab.isShowing()) {
                                        ab.show();
                                }
                        }
                });

                Button bHide = (Button) findViewById(R.id.bHideActionBar);
                bHide.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View v) {
                                // TODO Auto-generated method stub
                                if (ab.isShowing()) {
                                        ab.hide();
                                }
                        }
                });

                Button bAdd = (Button) findViewById(R.id.bAdd);
                bAdd.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View arg0) {
                                // TODO Auto-generated method stub
                                ab.addTab(ab
                                                .newTab()
                                                .setText("New Tab")
                                                .setTabListener(
                                                                new TabListener(new TabContentFragment())));
                        }

                });

                Button bRemove = (Button) findViewById(R.id.bRemove);
                bRemove.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View v) {
                                // TODO Auto-generated method stub
                                if (ab.getTabCount() > 0) {
                                        ab.removeTabAt(ab.getTabCount() - 1);
                                }
                        }
                });

                Button bRemoveAll = (Button) findViewById(R.id.bRemoveAll);
                bRemoveAll.setOnClickListener(new OnClickListener() {

                        @Override
                        public void onClick(View v) {
                                // TODO Auto-generated method stub
                                ab.removeAllTabs();
                        }
                });

        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
                // TODO Auto-generated method stub
                MenuInflater mMenuInflater = getMenuInflater();
                mMenuInflater.inflate(R.menu.main_activity_holo_dark_action_bar, menu);
                return true;
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
                // TODO Auto-generated method stub
                switch (item.getItemId()) {
                case android.R.id.home:
                        Intent intent = new Intent(this, ActionProviderActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent);
                        return true;
                case R.id.abForwardHoloDark:
                        Intent intent2 = new Intent(this, DropDownListActivity.class);
                        intent2.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent2);
                        return true;
                default:
                        return super.onOptionsItemSelected(item);
                }
        }

        private class TabListener implements ActionBar.TabListener {
                private TabContentFragment mFragment;

                public TabListener(TabContentFragment fragment) {
                        mFragment = fragment;
                }

                public void onTabSelected(Tab tab, FragmentTransaction ft) {
                        ft.add(R.id.flFragmentContent, mFragment, "New Tab");
                }

                public void onTabUnselected(Tab tab, FragmentTransaction ft) {
                        ft.remove(mFragment);
                }

                public void onTabReselected(Tab tab, FragmentTransaction ft) {
                        Toast.makeText(TabsActivity.this, "Reselected", Toast.LENGTH_SHORT)
                                        .show();
                }

        }

        private class TabContentFragment extends Fragment {

                @Override
                public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                Bundle savedInstanceState) {
                        View fragView = inflater.inflate(R.layout.tabscontent, container,
                                        false);

                        TextView text = (TextView) fragView.findViewById(R.id.tvTabText);
                        text.setText("Hello Tab");

                        return fragView;
                }

        }

}
Una riga di codice nuova è ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS), serve a indicare che si useranno le tabs.
Poi creo una prima tab nell'onCreate() attraverso ab.addTab(ab.newTab().setText("First Tab").setTabListener(new TabListener(new TabContentFragment()))). Ci saranno degli errori se non avete copiato e incollato l'intero codice. Non preoccupatevi. I primi due Button sono per mostrare e nascondere l'action bar, li ho messi in quest'activity perché prima mi ero dimenticato  O:-)
Il terzo Button, bAdd, crea una nuova tab. Il procedimento è lo stesso usato prima, cambia solo il testo usato come titolo della tab.
Il quarto Button, bRemove, serve per rimuovere l'ultima tab. Bisogna prima di tutto prendere il numero totale di tab, per esempio 10, e a questo sottrarre 1 in quanto il numero di ogni tab parte dallo 0. Quindi quella che noi vediamo come prima tab in realtà è la numero 0. Ecco spiegato il perché del -1.
Il quinto e ultimo Button, bRemoveAll, rimuove tutte le tabs.

Quando abbiamo chiamato setTabListener() abbiamo scritto new TabListener. La classe TabListener consiste in queste righe:
Codice (Java): [Seleziona]
private class TabListener implements ActionBar.TabListener {
                private TabContentFragment mFragment;

                public TabListener(TabContentFragment fragment) {
                        mFragment = fragment;
                }

                public void onTabSelected(Tab tab, FragmentTransaction ft) {
                        ft.add(R.id.flFragmentContent, mFragment, "New Tab");
                }

                public void onTabUnselected(Tab tab, FragmentTransaction ft) {
                        ft.remove(mFragment);
                }

                public void onTabReselected(Tab tab, FragmentTransaction ft) {
                        Toast.makeText(TabsActivity.this, "Reselected", Toast.LENGTH_SHORT)
                                        .show();
                }

        }
Consente di fare qualcosa quando una tab viene selezionata, deselezionata o riselezionata.
Quando una tab viene selezionata succede che si aggiunge nel FrameLayout chiamato flFragmentContent un Fragment chiamato mFragment. Vedremo dopo dove abbiamo preso questo mFragment.
Quando si deseleziona una tab succede che si rimuove mFragment.
Quando si riseleziona una tab succede che compare un Toast.

mFragment non nient'altro che un TabContentFragment, ovvero una sorta di Fragment modificato. La classe TabContentFragment consiste in queste righe:
Codice (Java): [Seleziona]
private class TabContentFragment extends Fragment {

                @Override
                public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                Bundle savedInstanceState) {
                        View fragView = inflater.inflate(R.layout.tabscontent, container,
                                        false);

                        TextView text = (TextView) fragView.findViewById(R.id.tvTabText);
                        text.setText("Hello Tab");

                        return fragView;
                }

        }
Se non avete letto la pagina relativa ai fragment cerco di spiegarvi cosa significa View fragView = inflater.inflate(R.layout.tabscontent, container, false): R.layout.tabscontent è la textview creata in un file xml, container è il "contenitore", la parent all'interno della quale sarà inserita fragView e il boolean false indica se fragView dovrebbe essere attaccata al container durante la creazione (inflation). Poi settiamo il testo della textview creata e ritorniamo la fragView. mFragment è appunto questa fragView.
Questa era forse la parte più difficile di tutto il tutorial, manca l'ultima activity ;)


L'ultima activity consiste nell'aggiungere una specie di spinner nell'action bar (come nel Calendario, in Gmail). È consigliato usare questa specie di spinner quando si vuole ordinare l'activity in un certo modo (giorno, settimana, mese).
Prima di tutto ho creato l'elenco delle possibili scelte (dropdownlist.xml) nella cartella values.
Eccolo qui:
Codice (XML): [Seleziona]
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string-array name="drop_down_spinner">
        <item>Day</item>
        <item>Week</item>
        <item>Month</item>
    </string-array>

</resources>
Potete ovviamente aggiungere più elementi semplicemente aggiungendo altri tag item.
Poi ho creato un layout che ho chiamato dropdown.xml:
Codice (XML): [Seleziona]
<?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:orientation="vertical" >

    <FrameLayout
       android:id="@+id/flDropDownContent"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</LinearLayout>
Il funzionamento sarà simile a quelle delle tabs, il FrameLayout flDropDownContent sarà il contenuto del Fragment.
Per l'action bar di questa activity non c'è bisogno di un file xml in quanto l'unico comando che aggiungerò è quella specie di spinner di cui ho parlato prima che si crea dinamicamente.
Questa è la classe DropDownListActivity:
Codice (Java): [Seleziona]
package com.design.zate;

import android.app.ActionBar;
import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.SpinnerAdapter;
import android.widget.TextView;

public class DropDownListActivity extends Activity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
                // TODO Auto-generated method stub
                super.onCreate(savedInstanceState);
                setContentView(R.layout.dropdown);
                ActionBar ab = getActionBar();
                ab.setHomeButtonEnabled(true);
                ab.setDisplayHomeAsUpEnabled(true);
                ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

                SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
                                R.array.drop_down_spinner,
                                android.R.layout.simple_spinner_dropdown_item);

                ab.setListNavigationCallbacks(mSpinnerAdapter,
                                new OnNavigationListener() {
                                        String[] strings = getResources().getStringArray(
                                                        R.array.drop_down_spinner);

                                        @Override
                                        public boolean onNavigationItemSelected(int position,
                                                        long itemId) {
                                                DropDownListContentFragment newFragment = new DropDownListContentFragment();
                                                FragmentTransaction ft = getFragmentManager()
                                                                .beginTransaction();

                                                ft.replace(R.id.flDropDownContent, newFragment,
                                                                strings[position]);

                                                ft.commit();
                                                return true;
                                        }

                                });
        }

        @Override
        public boolean onMenuItemSelected(int featureId, MenuItem item) {
                // TODO Auto-generated method stub
                switch (item.getItemId()) {
                case android.R.id.home:
                        Intent intent = new Intent(this, TabsActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        startActivity(intent);
                        return true;
                default:
                        return super.onMenuItemSelected(featureId, item);
                }
        }

        public class DropDownListContentFragment extends Fragment {

                String string;

                @Override
                public void onAttach(Activity activity) {
                        // TODO Auto-generated method stub
                        super.onAttach(activity);
                        string = getTag();
                }

                @Override
                public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                Bundle savedInstanceState) {
                        // TODO Auto-generated method stub
                        TextView tv = new TextView(getActivity());
                        tv.setText(string);
                        return tv;
                }

        }

}
La prima novità è ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST). Indica che si userà questo "spinner". Poi c'è lo SpinnerAdapter, richiede il context, l'array (vedi dropdownlist.xml sopra) il layout usato per creare la view. Settiamo le callbacks per lo spinner aggiungendo ab.setListNavigationCallbacks() al quale bisogna passare lo SpinnerAdapter e un onNavigationListener.
Nell'onNavigationListener bisogna aggiungere String[] strings = getResources().getStringArray(R.array.action_list) in modo da prendere le stesse stringhe usate nello SpinnerAdapter.
Si crea un DropDownListContentFragment, sempre un Fragment "modificato" (dopo spiegherò) e strings[position] è il tag che corrisponde alla stringa dell'array alla posizione indicata dall'integer position che ci viene passato dall'onNavigationItemSelected.
Con una FragmentTransaction si inizia la transazione (non so quanto sia corretta la traduzione :D).
ft.replace() consente appunto di cambiare il fragment: R.id.flDropDownContent è il contenitore del fragment, newFragment è il nuovo DropDownListContentFragment (ovvero un Fragment modificato) e infine si conclude la transazione con un ft.commit().
DropDownListContentFragment una classe che estende un Fragment. Le righe per crearla sono queste:
Codice (Java): [Seleziona]
        public class DropDownListContentFragment extends Fragment {

                String string;

                @Override
                public void onAttach(Activity activity) {
                        // TODO Auto-generated method stub
                        super.onAttach(activity);
                        string = getTag();
                }

                @Override
                public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                Bundle savedInstanceState) {
                        // TODO Auto-generated method stub
                        TextView tv = new TextView(getActivity());
                        tv.setText(string);
                        return tv;
                }

        }
Molto semplice: quando il fragment viene attaccato si prende il tag (che corrisponde alla stringa dell'array alla posizione indicata dall'integer position passato dall' onNavigationItemSelected, vedi sopra) e lo si assegna a una stringa. Si crea una TextView dinamicamente, si setta il testo della TextView con la stringa string e si ritorna la TextView.


THE END!
Se siete riusciti ad arrivare alla fine, vi faccio i più sinceri complimenti: adesso siete capaci di creare applicazioni con una grafica che vi farà sembrare esperti anche se avete cominciato a programmare da due settimane!  :-)

L'unica immagine che ho creato è quella dell'icona forward. Potete usarla come volete anche senza darmi i crediti.  Spero di non aver fatto errori, nel caso ce ne fossero fatemelo sapere :)
« Ultima modifica: 28 Gennaio 2012, 21:42:41 CET da Zate »

Offline Nicola_D

  • Utente storico
  • *****
  • Post: 2479
  • SBAGLIATO!
  • Respect: +323
    • Github
    • Google+
    • nicoladorigatti
    • Mostra profilo
  • Dispositivo Android:
    Nexus 6p, Nexus 4, Nexus S, Nexus 7(2012)
  • Sistema operativo:
    Windows 7
Re:[Facile]Android Design - Action Bar
« Risposta #1 il: 28 Gennaio 2012, 20:26:41 CET »
0
Azz,complimenti! Se ci metti pure gli screen è da stick!
IMPORTANTE:NON RISPONDO A PROBLEMI VIA MESSAGGIO PRIVATO
LOGCAT: Non sai cos'è? -> Android Debug Bridge | Android Developers
               Dov'è in Eclipse? -> Window -> Open Prospective -> DDMS e guarda in basso!
[Obbligatorio] Logcat, questo sconosciuto! (Gruppo AndDev.it LOGTFO) - Android Developers Italia

Zate

  • Visitatore
Re:[Facile]Android Design - Action Bar
« Risposta #2 il: 28 Gennaio 2012, 21:36:49 CET »
0
Sono onorato :) Potete approvare!
« Ultima modifica: 28 Gennaio 2012, 21:49:39 CET da Zate »

Offline Ricky`

  • Amministratore
  • Utente storico
  • *****
  • Post: 3487
  • Respect: +506
    • Github
    • Google+
    • rciovati
    • Mostra profilo
Re:[Facile]Android Design - Action Bar
« Risposta #3 il: 29 Gennaio 2012, 17:51:34 CET »
0
Sono onorato :) Potete approvare!

Ottimo tutorial, complimenti :) Se ti va metti il codice anche su github, così si può sfogliare anche senza scaricare l'archivio :)
Ho messo il topic in rilievo, lo merita :)

Zate

  • Visitatore
Re:[Facile]Android Design - Action Bar
« Risposta #4 il: 29 Gennaio 2012, 21:10:57 CET »
0

Offline Damix

  • Nuovo arrivato
  • *
  • Post: 13
  • Respect: 0
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy 2 - HTC Sensetion
  • Sistema operativo:
    Windows 7 - Windows 8
Re:[Facile]Android Design - Action Bar
« Risposta #5 il: 21 Febbraio 2012, 11:21:37 CET »
0
Sei un mito  ;) 

Offline frecciak

  • Utente normale
  • ***
  • Post: 231
  • Respect: +16
    • Mostra profilo
  • Dispositivo Android:
    galaxy s
  • Sistema operativo:
    windows 7
Re:[Facile]Android Design - Action Bar
« Risposta #6 il: 23 Febbraio 2012, 04:30:54 CET »
0
grazie mille per l'ottima guida
Se le risposte ti hanno aiutato, metti un ..a te non costa nulla, ma a chi ti ha aiutato fa molto piacere riceverlo!!

Offline Vlad

  • Utente normale
  • ***
  • Post: 271
  • Respect: +16
    • Github
    • Google+
    • vmihalachi
    • vmihalachi
    • Mostra profilo
    • vmihalachi.com
  • Dispositivo Android:
    Samsung galaxy s2 | Samsung galaxy tab 7
  • Play Store ID:
    Vlad Mihalachi
  • Sistema operativo:
    Windows 8
Re:[Facile]Android Design - Action Bar
« Risposta #7 il: 08 Marzo 2012, 15:58:31 CET »
0
Scusa la domanda stupida ma perche mi dice The type MyClass.TabListener must implement the inherited abstract method ActionBar.TabListener.onTabSelected(ActionBar$Tab) ???

Zate

  • Visitatore
Re:[Facile]Android Design - Action Bar
« Risposta #8 il: 08 Marzo 2012, 18:04:45 CET »
0
Mi sembra strano... Dovrebbe implementare onTabSelected(ActionBar$Tab, FragmentTransaction). Prova a postare il codice della tua classe che implementa ActionBar.TabListener (che io ho chiamato TabListener)

Offline Vlad

  • Utente normale
  • ***
  • Post: 271
  • Respect: +16
    • Github
    • Google+
    • vmihalachi
    • vmihalachi
    • Mostra profilo
    • vmihalachi.com
  • Dispositivo Android:
    Samsung galaxy s2 | Samsung galaxy tab 7
  • Play Store ID:
    Vlad Mihalachi
  • Sistema operativo:
    Windows 8
Re:[Facile]Android Design - Action Bar
« Risposta #9 il: 09 Marzo 2012, 14:34:40 CET »
0
Scusate avevo implementato male il tutorial  :-)

Offline nicopasso

  • Nuovo arrivato
  • *
  • Post: 32
  • Respect: +1
    • Mostra profilo
  • Dispositivo Android:
    Google Nexus 5
  • Sistema operativo:
    Windows 7, Mac OS X 10.5
Re:[Facile]Android Design - Action Bar
« Risposta #10 il: 25 Marzo 2012, 14:24:10 CEST »
0
Ottimo tutorial, chiaro ed esplicativo al 100%.  8-)

Offline teonji

  • Nuovo arrivato
  • *
  • Post: 7
  • Respect: 0
    • Mostra profilo
Re:[Facile]Android Design - Action Bar
« Risposta #11 il: 29 Marzo 2012, 18:26:00 CEST »
0
ciao a tutti, volevo sapere se era possibile usare action bar nella mia app che sto sviluppando però per 2.3.3 per avere più compatibilità..
qualcuno oggi mi ha detto che le tabActivity sono deprecate.. >:(

Offline Ricky`

  • Amministratore
  • Utente storico
  • *****
  • Post: 3487
  • Respect: +506
    • Github
    • Google+
    • rciovati
    • Mostra profilo
R: Re:[Facile]Android Design - Action Bar
« Risposta #12 il: 29 Marzo 2012, 18:53:48 CEST »
0
ciao a tutti, volevo sapere se era possibile usare action bar nella mia app che sto sviluppando però per 2.3.3 per avere più compatibilità..
qualcuno oggi mi ha detto che le tabActivity sono deprecate.. >:(

Dai un occhio alla libreria Actionbar Sherlock 4.

Inviato dal mio HTC Desire usando Tapatalk

Offline teonji

  • Nuovo arrivato
  • *
  • Post: 7
  • Respect: 0
    • Mostra profilo
Re:[Facile]Android Design - Action Bar
« Risposta #13 il: 29 Marzo 2012, 20:58:01 CEST »
0
grazie prof!  :-P

Offline teonji

  • Nuovo arrivato
  • *
  • Post: 7
  • Respect: 0
    • Mostra profilo
Re:[Facile]Android Design - Action Bar
« Risposta #14 il: 31 Marzo 2012, 17:28:22 CEST »
0
ho provato ad installarla ma mi da sempre errori..non mi carica il manifest e se glielo importo a parte io dopo, cmq gli mancano dei file per poterli usare come libreria..ho visto che è un problema abbastanza comune ma non trovo tra le soluzioni un modo per farla funzionare a me..avete qualche info a riguardo?

edit: ora sono riuscito a fargliela importare correggendo tutti gli errori di @override ma appena importo la libreria, ora funzionante, al mio progetto, il mio progetto non mi riconosce più R.style.qualcosa ecc..
« Ultima modifica: 31 Marzo 2012, 18:08:05 CEST da teonji »