Górne menu- Toolbar
W aplikacjach mobilnych opartych na systemie Android górne menu (Toolbar) stanowi kluczowy element interfejsu użytkownika. Umożliwia ono szybki dostęp do najważniejszych funkcji aplikacji, takich jak ustawienia, pomoc, informacje o programie czy wyjście. W odróżnieniu od klasycznego ActionBar, Toolbar jest komponentem bardziej elastycznym i konfigurowalnym, co pozwala na jego pełne dostosowanie do wyglądu i potrzeb aplikacji.
Podczas tej lekcji nauczysz się, jak utworzyć i osadzić własny Toolbar w aplikacji stworzonej w Android Studio z użyciem języka Kotlin. Dowiesz się, jak zbudować plik XML definiujący zawartość menu, jak obsłużyć kliknięcia w jego elementy oraz jak dostosować jego wygląd do kolorystyki aplikacji. Krok po kroku prześledzisz cały proces tworzenia funkcjonalnego i estetycznego górnego paska narzędzi.
Kontrolka Toolbar
Kontrolka Toolbar to nowoczesny i bardziej konfigurowalny odpowiednik klasycznego paska akcji (ActionBar). Umożliwia osadzanie przycisków, ikon, tytułu oraz rozwijanego menu w górnej części aplikacji. Aby móc z niej skorzystać, należy najpierw odwołać się do niej z poziomu kodu Kotlin.
Układ widoku aplikacji będzie się składać z trzech elementów:
- LinearLayout (układ pionowy)
- Toolbar
- TextView
Marginesy dobierz według własnego uznania.

W pierwszym kroku tworzymy zmienną, która będzie reprezentować Toolbar zdefiniowany w pliku activity_main.xml. Użycie funkcji findViewById() pozwala przypisać widok Toolbar'u do zmiennej w kodzie. W tym momencie może jeszcze pojawić się błąd kompilacji, ponieważ brakuje odpowiedniego importu klasy Toolbar.
Tworzymy zmienną odwołującą się do kontrolki Toolbar. Jej inicjacja wywołuje komunikat błędu.

Importujemy wymaganą bibliotekę
Wskazówka:
import androidx.appcompat.widget.Toolbar
Własny Toolbar zdefiniowany w XML wymaga integracji z systemowym menu oraz nawigacją. W tym celu stosuje się metodę setSupportActionBar(). Odpowiedni fragment kodu
Wskazówka:
// Ustawienie toolbaru jako ActionBar
val toolbar = findViewById(R.id.my_toolbar)
setSupportActionBar(toolbar)
Od tej pory tworzony Toolbar jest traktowany jako wbudowany ActionBar, czyli podstawowy pasek narzędzi w ramach danej aktywności, na którym może być wyświetlany tytuł aktywności, opcje nawigacji na poziomie aplikacji i inne interaktywne elementy.
Tworzenie zawartości menu paska Toolbar
Aby Toolbar mógł wyświetlać własne przyciski i opcje, należy przygotować specjalny plik menu w formacie XML. Plik ten zawiera definicje wszystkich elementów, które mają pojawić się na pasku narzędzi, takich jak ikony, nazwy opcji czy ich pozycja (na pasku lub w rozwijanym menu).
Plik menu umieszczamy w katalogu res/menu, który może być utworzony ręcznie lub za pomocą kreatora. Z poziomu Android Studio wystarczy kliknąć prawym przyciskiem myszy na folder res, a następnie wybrać New ? Android Resource File. W nowym oknie podajemy nazwę pliku (np. menu_toolbar.xml) i wybieramy typ zasobu menu.
W hierarchii projektu wskaż folder res i z menu prawego przycisku myszki wybierz New/ Android Resources File


Prawidłowo wykonane kroki zmieniają układ hierarchii projektu- pojawia się nowy folder wraz z plikiem XML, w którym tworzy się zawartość menu.

Przykładowo taka prosta zawartość pliku menu_toolbar.xml
Wskazówka:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_settings"
android:title="Ustawienia"/>
<item
android:id="@+id/menu_help"
android:title="Pomoc"/>
<item android:title="Item" />
</menu>
Tworzy menu o postaci

Jeżeli chcemy aby elementy menu były w miarę możliwości zawarte w górnym pasku na przykład w postaci ikon, to wprowadzamy poniższe zmiany do pliku XML zawartości menu
Wskazówka:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_settings"
android:title="Ustawienia"
android:icon="@android:drawable/ic_menu_preferences"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_help"
android:title="Pomoc"
android:icon="@android:drawable/ic_menu_help"
app:showAsAction="ifRoom" />
<item
android:id="@+id/menu_zamknij"
android:title="Zamknij"/>
</menu>

Atrybut app:showAsAction w pliku menu XML decyduje o tym, gdzie i w jaki sposób dany element menu zostanie wyświetlony w aplikacji.
Ustawienie app:showAsAction="ifRoom" oznacza, że element menu zostanie pokazany bezpośrednio na pasku Toolbar tylko wtedy, gdy będzie na to wystarczająco dużo miejsca. Jeśli miejsca zabraknie, element zostanie automatycznie przeniesiony do rozwijanego menu (ikona z trzema kropkami).
Z kolei app:showAsAction="never" wymusza umieszczenie elementu wyłącznie w rozwijanym menu, niezależnie od ilości dostępnego miejsca na pasku. Tego atrybutu używa się zwykle dla mniej używanych funkcji lub takich, które nie wymagają natychmiastowego dostępu.
Dzięki tym ustawieniom mamy pełną kontrolę nad rozmieszczeniem opcji menu i ich widocznością w interfejsie użytkownika.
Przykład modyfikacji zawartości pliku XML o dwa kolejne elementy menu, które znajdą się w rozwijanym menu.
Wskazówka:
<!-- Te element tylko w rozwijanym menu -->
<item
android:id="@+id/menu_o_aplikacji"
android:title="O aplikacji"
app:showAsAction="never" />
<item
android:id="@+id/menu_zamknij"
android:title="Zamknij"
app:showAsAction="never" />
Wczytanie menu z pliku XML
Aby wyświetlić elementy menu w Toolbarze, Android musi wczytać ich definicję z przygotowanego pliku XML (np. menu_toolbar.xml). Do tego celu służy MenuInflater ? klasa, która ?nadmuchuje? (inflatuje) strukturę XML i przekształca ją w obiekt Menu, który następnie jest renderowany w interfejsie.
W głównym pliku importujemy bibliotekę obsługi menu (android.view.Menu)
Wskazówka:
import android.os.Bundle
import android.view.Menu
import androidx.appcompat.widget.Toolbar
Kolejnym krokiem jest nadpisanie metody override fun onCreateOptionsMenu(), która automatycznie wywoływana jest przy tworzeniu Toolbaru. W tej metodzie menuInflater.inflate(R.menu.menu_toolbar, menu) wczytuje zawartość naszego pliku menu_toolbar.xml do menu w aplikacji.
Przykładowa implementacja wygląda tak:
Wskazówka:
class MainActivity : AppCompatActivity() {
// Inflacja menu - wczytanie elementów menu z pliku XML
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_toolbar, menu)
return true
}
}
Po skompilowaniu aplikacji zostanie załadowane nowe menu.

Zmiana koloru tła i ikon Toolbaru
W pliku colors.xml definiujemy kolor tła obszaru paska Toolbar
Wskazówka:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="zolty">#FFC107</color>
</resources>
Przechodzimy do pliku układu głównego widoku activity_main.xml wprowadzamy poniższe zmiany dla elementu Toolbar
Wskazówka:
<androidx.appcompat.widget.Toolbar
android:id="@+id/my_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp"
app:title="@string/moj_toolbar"
android:background="@color/zolty"
android:theme="@style/ThemeOverlay.AppCompat.Light"
app:titleTextColor="@android:color/white"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
Kompilujemy aplikację i sprawdzamy wygląd menu.

Kolor tła został zmieniony ale nie są widoczne ikony menu rozwijanego (overflow menu) po kliknięciu w ikonę trzech kropek. Dzieje się tak, ponieważ należy wymusić w kodzie programu wyświetlenie tych ikon. Rozwiązanie to jest dostępne dla API 11 i wzwyż. Wprowadź poniższe zmiany w pliku MainActivity.kt
Wskazówka:
// Inflacja menu - wczytanie elementów menu z pliku XML
@SuppressLint("RestrictedApi")
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_toolbar, menu)
// Wymuszenie ikon w overflow menu (działa od API 11 wzwyż)
if (menu is androidx.appcompat.view.menu.MenuBuilder) {
menu.setOptionalIconsVisible(true)
}
return true
}
Uruchom aplikację i sprawdź efekt działania

Obsługa elementów menu
Obsługę kliknięcia elementów menu przedstawimy jako wyświetlenie krótkiego komunikatu. Jedynie w opcji Zamknij zrealizujemy rzeczywiste zamkniecie programu. Do pliku MainActivity.kt dopisujemy funkcję wyświetlającą komunikat.
Wskazówka:
private fun pokazKomunikat(tekstKomunikatu: String) {
android.widget.Toast.makeText(this, tekstKomunikatu, android.widget.Toast.LENGTH_SHORT).show()
}
Obsługę kliknięcia w elementy menu realizuje nadpisanie metody onOptionsItemSelected(), której argumentami są elementy menu.
Wskazówka:
override fun onOptionsItemSelected(item: android.view.MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_settings -> {
// Obsługa kliknięcia ?Ustawienia?
pokazKomunikat("Kliknięto: Ustawienia")
true
}
R.id.menu_help -> {
pokazKomunikat("Kliknięto: Pomoc")
true
}
R.id.menu_o_aplikacji -> {
pokazKomunikat("Kliknięto: O aplikacji")
true
}
R.id.menu_zamknij -> {
pokazKomunikat("Kliknięto: Zamknij")
finishAffinity() // Zamyknij wszystkie aktywności
true
}
else -> super.onOptionsItemSelected(item)
}
}
Uruchom aplikację i sprawdź efekt działania.

Zmiana tła wybranego elementu menu
W Androidzie elementy menu w menu rozwijanym (overflow) nie wspierają bezpośrednio atrybutu android:background w XML. Czyli konstrukcja android:background="@color/zolty" zapisana w pliku XML budowy menu nie będzie obsłużona.
Problem można rozwiązać na przykład bezpośrednio w kodzie nadpisując tworzone menu aplikacji. W tym celu tworzy się zmienną wykorzystującą łącznik do elementu, do którego można dołączać znaczniki.
Wskazówka:
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_toolbar, menu)
// Wymuszenie ikon w overflow menu (działa od API 11 wzwyż)
if (menu is androidx.appcompat.view.menu.MenuBuilder) {
menu.setOptionalIconsVisible(true)
}
// Znajdź konkretny element menu i ustaw mu kolor tła
val item = menu!!.findItem(R.id.menu_zamknij)
//utwórz łacznik do obiektu do którego mozna dołaczac znaczniki
val span = android.text.SpannableString(item.title)
span.setSpan(
android.text.style.BackgroundColorSpan(
resources.getColor(R.color.zolty, theme)
),
0, span.length,
android.text.Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
item.title = span
return true
}

Zmiana tła całego elementu w rozwijanym menu (overflow) to jedno z trudniejszych zadań, ponieważ Android nie udostępnia oficjalnie API (na dzień w którym ten tekst powstał), które by na to pozwalało. Ale jest sposób, który działa ? z dostępem do wewnętrznych widoków popup menu przez refleksję.
Technika użyta w tym przypadku to tzw. refleksja (ang. reflection) ? zaawansowany mechanizm języka Kotlin (i Javy), który pozwala na dostęp do elementów klas w czasie działania programu, nawet jeśli są one prywatne lub normalnie niedostępne przez standardowe API.
Dzięki refleksji możemy m.in.:
- odczytać prywatne pola i metody klasy,
- dynamicznie je wywoływać lub modyfikować,
- uzyskać dostęp do wewnętrznych struktur systemu Android (jak ukryte komponenty menu),
- tworzyć elastyczne rozwiązania tam, gdzie nie istnieje oficjalne wsparcie w API.
W przypadku rozwijanego menu (overflow) Android nie udostępnia publicznego sposobu na zmianę tła pojedynczego elementu. Dzięki refleksji można ?zajrzeć? do struktury, która buduje rozwijane menu, a następnie ręcznie zmienić tło wybranego wiersza, korzystając z jego obiektu widoku (View).
Należy jednak pamiętać, że użycie refleksji wiąże się z pewnym ryzykiem:
- może przestać działać w przyszłych wersjach Androida (gdy zmieni się wewnętrzna struktura klas),
- może być mniej wydajne,
- wymaga dokładnej znajomości nazw pól/metod i ostrożności w ich użyciu.
Mimo to, refleksja jest potężnym narzędziem, zwłaszcza tam, gdzie standardowe możliwości są ograniczone.
Pełny kod pliku MainActivity.kt
Wskazówka:
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.Menu
import androidx.appcompat.widget.Toolbar
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top,
systemBars.right, systemBars.bottom)
insets
}
// Ustawienie toolbaru jako ActionBar
val toolbar = findViewById<Toolbar>(R.id.my_toolbar)
setSupportActionBar(toolbar)
}
// Inflacja menu - wczytanie elementów menu z pliku XML
@SuppressLint("RestrictedApi")
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_toolbar, menu)
// Wymuszenie ikon w overflow menu (działa od API 11 wzwyż)
if (menu is androidx.appcompat.view.menu.MenuBuilder) {
menu.setOptionalIconsVisible(true)
}
// Znajdź konkretny element menu i ustaw mu kolor tła
val item = menu!!.findItem(R.id.menu_zamknij)
//utwórz łacznik do obiektu do którego mozna dołaczac znaczniki
val span = android.text.SpannableString(item.title)
span.setSpan(
android.text.style.BackgroundColorSpan(
resources.getColor(R.color.zolty, theme)
),
0, span.length,
android.text.Spannable.SPAN_INCLUSIVE_INCLUSIVE
)
item.title = span
return true
}
override fun onOptionsItemSelected(item: android.view.MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_settings -> {
// Obsługa kliknięcia ?Ustawienia?
pokazKomunikat("Kliknięto: Ustawienia")
true
}
R.id.menu_help -> {
pokazKomunikat("Kliknięto: Pomoc")
true
}
R.id.menu_o_aplikacji -> {
pokazKomunikat("Kliknięto: O aplikacji")
true
}
R.id.menu_zamknij -> {
pokazKomunikat("Kliknięto: Zamknij")
finishAffinity() // Zamyknij wszystkie aktywności
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun pokazKomunikat(tekstKomunikatu: String) {
android.widget.Toast.makeText(this, tekstKomunikatu,
android.widget.Toast.LENGTH_SHORT).show()
}
}