Utilizzo di AppBar

La barra superiore dell'app fornisce una posizione coerente nella parte superiore della finestra dell'app per visualizzare le informazioni e le azioni della schermata corrente.

Esempio di barra superiore dell'app
Figura 1. Un esempio della barra superiore dell'app.

La proprietà della barra dell'app varia a seconda delle esigenze dell'app. Quando si utilizzano i frammenti, la barra dell'app può essere implementata come un elemento ActionBar di proprietà dell'attività host o come barra degli strumenti all'interno del layout del frammento.

Se tutti i tuoi schermi utilizzano la stessa barra dell'app, sempre in alto e che si estende per tutta la larghezza dello schermo, utilizza una barra delle azioni fornita dal tema e ospitata dall'attività. L'utilizzo delle barre delle app a tema consente di mantenere un aspetto coerente e offre uno spazio in cui ospitare i menu opzioni e un pulsante Su.

Utilizza una barra degli strumenti ospitata dal frammento se vuoi un maggiore controllo su dimensioni, posizionamento e animazione della barra dell'app su più schermate. Ad esempio, potresti aver bisogno di una barra dell'app che comprimi o si estende solo per metà della larghezza dello schermo ed è centrata verticalmente.

Situazioni diverse richiedono approcci diversi, ad esempio per gonfiare i menu e rispondere all'interazione dell'utente. Capire i diversi approcci e utilizzare quello migliore per la tua app consente di risparmiare tempo e di garantire che l'app funzioni correttamente.

Gli esempi in questo argomento fanno riferimento a un ExampleFragment che contiene un profilo modificabile. Il frammento aumenta il seguente menu definito XML nella barra dell'app:

<!-- sample_menu.xml -->
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_settings"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/action_done"
        android:icon="@drawable/ic_done"
        android:title="@string/done"
        app:showAsAction="ifRoom|withText"/>

</menu>

Il menu contiene due opzioni: una per accedere alla schermata di un profilo e una per salvare le modifiche apportate al profilo.

Barra dell'app di proprietà dell'attività

La barra delle app è più comunemente utilizzata dall'attività dell'host. Quando la barra dell'app è di proprietà di un'attività, i frammenti possono interagire con la barra ignorando i metodi del framework richiamati durante la creazione dei frammenti.

Registrati con l'attività

Devi comunicare al sistema che il frammento della barra dell'app fa parte della compilazione del menu opzioni. Per farlo, chiama setHasOptionsMenu(true) nel metodo onCreate(Bundle) del frammento, come mostrato nell'esempio seguente:

Kotlin

class ExampleFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }
}

Java

public class ExampleFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }
}

setHasOptionsMenu(true) indica al sistema che il frammento vuole ricevere callback relativi ai menu. Quando si verifica un evento relativo al menu, ad esempio un clic, il metodo di gestione degli eventi viene chiamato inizialmente nell'attività e poi nel frammento.

Tuttavia, non fare affidamento su questo ordine nella logica dell'applicazione. Se la stessa attività ospita più frammenti, ogni frammento può fornire opzioni di menu, nel qual caso l'ordine di callback dipende dall'ordine in cui vengono aggiunti i frammenti.

Amplia il menu

Per unire il menu a quello della barra dell'app, esegui l'override di onCreateOptionsMenu() nel frammento. Questo metodo riceve l'attuale menu della barra dell'app e un MenuInflater come parametri. Utilizza il pulsante di gonfiaggio dei menu per creare un'istanza del menu del frammento, quindi uniscila al menu corrente, come mostrato nell'esempio seguente:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.sample_menu, menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
       inflater.inflate(R.menu.sample_menu, menu);
    }
}

La figura 2 mostra il menu aggiornato.

il menu opzioni ora contiene il frammento di menu
Figura 2. Il menu opzioni ora contiene il frammento di menu.

Gestire gli eventi di clic

Ogni attività e frammento che partecipa al menu opzioni risponde ai tocchi. L'elemento onOptionsItemSelected() del frammento riceve la voce di menu selezionata come parametro e restituisce un valore booleano che indica se viene consumato il tocco. Quando un'attività o un frammento restituisce true da onOptionsItemSelected(), nessun altro frammento partecipante riceverà il callback.

Nell'implementazione di onOptionsItemSelected(), utilizza un'istruzione switch in itemId della voce di menu. Se l'elemento selezionato è tuo, gestisci il tocco in modo appropriato e restituisci true per indicare che viene gestito l'evento di clic. Se l'elemento selezionato non è tuo, chiama l'implementazione super. Per impostazione predefinita, l'implementazione super restituisce false per continuare l'elaborazione del menu.

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                // Navigate to settings screen.
                true
            }
            R.id.action_done -> {
                // Save profile changes.
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:  {
                // Navigate to settings screen.
                return true;
            }
            case R.id.action_done: {
                // Save profile changes.
                return true;
            }
            default:
                return super.onOptionsItemSelected(item);
        }

    }

}

Modificare in modo dinamico il menu

Inserisci la logica per nascondere o mostrare un pulsante o modificare l'icona in onPrepareOptionsMenu(). Questo metodo viene richiamato subito prima della visualizzazione del menu.

Continuando con l'esempio precedente, il pulsante Salva dovrebbe essere invisibile finché l'utente non inizia a modificarlo e dovrebbe scomparire dopo il salvataggio. L'aggiunta di questa logica a onPrepareOptionsMenu() rende il menu presente correttamente:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onPrepareOptionsMenu(menu: Menu){
        super.onPrepareOptionsMenu(menu)
        val item = menu.findItem(R.id.action_done)
        item.isVisible = isEditing
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onPrepareOptionsMenu(@NonNull Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item = menu.findItem(R.id.action_done);
        item.setVisible(isEditing);
    }
}

Quando devi aggiornare il menu, ad esempio quando un utente preme il pulsante Modifica per modificare le informazioni del profilo, chiama invalidateOptionsMenu() sull'attività dell'host per richiedere che il sistema chiami onCreateOptionsMenu(). Dopo l'annullamento della convalida, puoi apportare gli aggiornamenti in onCreateOptionsMenu(). Quando il menu aumenta, il sistema chiama onPrepareOptionsMenu() e aggiorna il menu per riflettere lo stato attuale del frammento.

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateOptionsMenu() {
        isEditing = !isEditing
        requireActivity().invalidateOptionsMenu()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateOptionsMenu() {
        isEditing = !isEditing;
        requireActivity().invalidateOptionsMenu();
    }
}

Barra dell'app di proprietà di un frammento

Se la maggior parte delle schermate dell'app non ha bisogno di una barra dell'app o se una schermata richiede una barra dell'app diversa rispetto alle altre, puoi aggiungere un elemento Toolbar al layout dei frammenti. Anche se puoi aggiungere un elemento Toolbar in qualsiasi punto della gerarchia delle visualizzazioni del frammento, in genere lo mantieni nella parte superiore dello schermo. Per utilizzare Toolbar nel frammento, fornisci un ID e ottieni un riferimento nel frammento, come con qualsiasi altra vista. Puoi anche valutare la possibilità di animare la barra degli strumenti utilizzando i comportamenti di CoordinatorLayout.

<androidx.appcompat.widget.Toolbar
    android:id="@+id/myToolbar"
    ... />

Se utilizzi una barra delle app di proprietà di frammenti, Google consiglia di usare direttamente le API Toolbar. Non utilizzare setSupportActionBar() e le API menu Fragment, che sono appropriate solo per le barre delle app di proprietà delle attività.

Amplia il menu

Il metodo di praticità Toolbar inflateMenu(int) prende l'ID di una risorsa del menu come parametro. Per gonfiare una risorsa del menu XML nella barra degli strumenti, passa resId a questo metodo, come mostrato nell'esempio seguente:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu);
    }

}

Per gonfiare un'altra risorsa del menu XML, chiama di nuovo il metodo con resId del nuovo menu. Le nuove voci vengono aggiunte al menu e le voci esistenti non vengono modificate né rimosse.

Se vuoi sostituire il set di menu esistente, cancella il menu prima di chiamare inflateMenu(int) con il nuovo ID menu, come illustrato nell'esempio seguente:

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun clearToolbarMenu() {
        viewBinding.myToolbar.menu.clear()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void clearToolbarMenu() {

        viewBinding.myToolbar.getMenu().clear()

    }

}

Gestire gli eventi di clic

Puoi passare una OnMenuItemClickListener direttamente alla barra degli strumenti con il metodo setOnMenuItemClickListener(). Questo listener viene richiamato quando l'utente seleziona una voce di menu dai pulsanti di azione presentati alla fine della barra degli strumenti o dall'overflow associato. Il valore MenuItem selezionato viene passato al metodo onMenuItemClick() del listener e può essere utilizzato per utilizzare l'azione, come mostrato nell'esempio seguente:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener {
            when (it.itemId) {
                R.id.action_settings -> {
                    // Navigate to settings screen.
                    true
                }
                R.id.action_done -> {
                    // Save profile changes.
                    true
                }
                else -> false
            }
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener(item -> {
            switch (item.getItemId()) {
                case R.id.action_settings:
                    // Navigate to settings screen.
                    return true;
                case R.id.action_done:
                    // Save profile changes.
                    return true;
                default:
                    return false;
            }
        });
    }
}

Modificare in modo dinamico il menu

Se la barra dell'app è di proprietà del frammento, puoi modificare Toolbar in fase di runtime esattamente come qualsiasi altra vista.

Continuando con l'esempio precedente, l'opzione di menu Salva dovrebbe essere invisibile fino a quando l'utente non inizia la modifica e dovrebbe scomparire di nuovo quando l'ha toccata:

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateToolbar() {
        isEditing = !isEditing

        val saveItem = viewBinding.myToolbar.menu.findItem(R.id.action_done)
        saveItem.isVisible = isEditing

    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateToolbar() {
        isEditing = !isEditing;

        MenuItem saveItem = viewBinding.myToolbar.getMenu().findItem(R.id.action_done);
        saveItem.setVisible(isEditing);
    }

}

Se presente, il pulsante di navigazione viene visualizzato all'inizio della barra degli strumenti. Se imposti un'icona di navigazione sulla barra degli strumenti, questa diventa visibile. Puoi anche impostare un elemento onClickListener() specifico per la navigazione che viene chiamato ogni volta che l'utente fa clic sul pulsante di navigazione, come mostrato nell'esempio seguente:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        myToolbar.setNavigationIcon(R.drawable.ic_back)

        myToolbar.setNavigationOnClickListener { view ->
            // Navigate somewhere.
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setNavigationIcon(R.drawable.ic_back);
        viewBinding.myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Navigate somewhere.
            }
        });
    }
}