school-it.pl // android + kotlin // rozdział 3 // lekcja 11
Switch — włącz / wyłącz
Switch (przełącznik) wygląda jak suwak włącznika światła. Logicznie działa tak samo jak CheckBox — ma dwa stany — ale mówi użytkownikowi coś innego: zmiana działa od razu, bez przycisku „Zapisz”. Dlatego przełączniki spotkasz głównie w ustawieniach. W tej lekcji zbudujesz ekran ustawień z podglądem, który zmienia się w chwili przesunięcia przełącznika.
Czego się dziś nauczysz
- Wybierzesz właściwą klasę przełącznika dla motywu Material 3.
- Ustawisz stan początkowy i podpis przełącznika.
- Zareagujesz na przełączenie od razu zmieniając wygląd ekranu.
- Uzależnisz jeden przełącznik od drugiego.
- Odróżnisz sytuacje dla
Switchi dlaCheckBox.
Trzy przełączniki Androida
Android przez lata dorobił się trzech wersji przełącznika. Wszystkie działają tak samo w kodzie, różnią się wyglądem:
| Znacznik XML | Kiedy |
|---|---|
<Switch> | Najstarszy, z samego Androida. Wygląda inaczej niż reszta aplikacji Material 3. Android Studio podkreśla go ostrzeżeniem. |
<androidx.appcompat.widget.SwitchCompat> | Wersja z biblioteki AppCompat — w starszych projektach. |
<com.google.android.material.materialswitch.MaterialSwitch> | Ten używamy. Przełącznik Material 3 pasujący do motywu nowych projektów. |
Długa nazwa to pełna ścieżka do klasy z biblioteki Material, którą szablon projektu ma już dołączoną. Android Studio podpowie ją po wpisaniu <MaterialSw.
MaterialSwitch w XML
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/swPowiadomienia"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="Powiadomienia" />
Przy szerokości match_parent podpis jest po lewej, a suwak przy prawej krawędzi — tak wyglądają ustawienia w telefonie. Cały wiersz reaguje na dotyk.
Przełącznik w kodzie
W kodzie przełącznik zachowuje się dokładnie jak CheckBox z lekcji 09 — oba dziedziczą po tej samej klasie CompoundButton („przycisk dwustanowy”):
val swCiemny = findViewById<MaterialSwitch>(R.id.swCiemny)
swCiemny.setOnCheckedChangeListener { _, wlaczony ->
if (wlaczony) {
karta.setBackgroundColor(Color.parseColor("#1D1B20"))
} else {
karta.setBackgroundColor(Color.WHITE)
}
}
swCiemny.isChecked = true // zmiana z kodu — listener też się wykona
Klasa MaterialSwitch wymaga importu com.google.android.material.materialswitch.MaterialSwitch — Alt+Enter doda go sam.
Switch czy CheckBox?
| Switch | CheckBox | |
|---|---|---|
| skutek | działa natychmiast | zatwierdzany przyciskiem |
| znaczenie | włącz / wyłącz funkcję | wybierz z listy / zgódź się |
| przykład | Wi-Fi, tryb ciemny, dźwięki | dodatki do pizzy, zgoda na regulamin |
Jeśli po przesunięciu przełącznika użytkownik musi jeszcze kliknąć „Zapisz”, to znak, że powinien to być CheckBox.
Ustawienia z podglądem
Na górze ekranu jest podgląd — karta z przykładową wiadomością. Pod nią cztery przełączniki: tryb ciemny zmienia kolory karty, duża czcionka zmienia rozmiar tekstu, „Pokaż godzinę” chowa i pokazuje godzinę wiadomości, a „Powiadomienia” włącza drugi, zależny przełącznik „Dźwięk powiadomień”. Każda zmiana działa od razu.
trzech
przełączników
przełącznik „Dźwięk” działa tylko przy włączonych powiadomieniach
Przygotowanie projektu
- Nowy projekt Empty Views Activity, nazwa
Ustawienia, Kotlin, API 24. - Podmień
activity_main.xmliMainActivity.kt.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="16dp"
android:fitsSystemWindows="true">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ustawienia"
android:textSize="24sp"
android:textStyle="bold" />
<!-- podgląd wiadomości -->
<LinearLayout
android:id="@+id/karta"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:background="#FFFFFF"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvNadawca"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Anna"
android:textStyle="bold" />
<TextView
android:id="@+id/tvTresc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Jutro sprawdzian z Kotlina — powtarzamy razem?" />
<TextView
android:id="@+id/tvGodzina"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:text="20:58"
android:textSize="12sp" />
</LinearLayout>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/swCiemny"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Tryb ciemny" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/swDuza"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Duża czcionka" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/swGodzina"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="Pokaż godzinę" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/swPowiadomienia"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Powiadomienia" />
<!-- zależny: wcięty i nieaktywny, dopóki powiadomienia są wyłączone -->
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/swDzwiek"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:enabled="false"
android:text="Dźwięk powiadomień" />
<TextView
android:id="@+id/tvStan"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="14sp" />
</LinearLayout>
package pl.schoolit.ustawienia
import android.graphics.Color
import android.os.Bundle
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import com.google.android.material.materialswitch.MaterialSwitch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val karta = findViewById<LinearLayout>(R.id.karta)
val tvNadawca = findViewById<TextView>(R.id.tvNadawca)
val tvTresc = findViewById<TextView>(R.id.tvTresc)
val tvGodzina = findViewById<TextView>(R.id.tvGodzina)
val tvStan = findViewById<TextView>(R.id.tvStan)
val swCiemny = findViewById<MaterialSwitch>(R.id.swCiemny)
val swDuza = findViewById<MaterialSwitch>(R.id.swDuza)
val swGodzina = findViewById<MaterialSwitch>(R.id.swGodzina)
val swPowiadomienia = findViewById<MaterialSwitch>(R.id.swPowiadomienia)
val swDzwiek = findViewById<MaterialSwitch>(R.id.swDzwiek)
// opis stanu powiadomień pod przełącznikami
fun pokazStanPowiadomien() {
if (!swPowiadomienia.isChecked) {
tvStan.text = "Powiadomienia wyłączone"
} else if (swDzwiek.isChecked) {
tvStan.text = "Powiadomienia: z dźwiękiem"
} else {
tvStan.text = "Powiadomienia: bez dźwięku"
}
}
pokazStanPowiadomien()
swCiemny.setOnCheckedChangeListener { _, wlaczony ->
if (wlaczony) {
karta.setBackgroundColor(Color.parseColor("#1D1B20"))
tvNadawca.setTextColor(Color.WHITE)
tvTresc.setTextColor(Color.parseColor("#E6E0E9"))
tvGodzina.setTextColor(Color.parseColor("#CAC4D0"))
} else {
karta.setBackgroundColor(Color.WHITE)
tvNadawca.setTextColor(Color.BLACK)
tvTresc.setTextColor(Color.BLACK)
tvGodzina.setTextColor(Color.GRAY)
}
}
swDuza.setOnCheckedChangeListener { _, wlaczony ->
val rozmiar = if (wlaczony) 20f else 14f
tvNadawca.textSize = rozmiar
tvTresc.textSize = rozmiar
tvGodzina.textSize = rozmiar - 2
}
swGodzina.setOnCheckedChangeListener { _, wlaczony ->
tvGodzina.isVisible = wlaczony // false: napis znika
}
swPowiadomienia.setOnCheckedChangeListener { _, wlaczony ->
swDzwiek.isEnabled = wlaczony
if (!wlaczony) {
swDzwiek.isChecked = false // bez powiadomień nie ma dźwięku
}
pokazStanPowiadomien()
}
swDzwiek.setOnCheckedChangeListener { _, _ ->
pokazStanPowiadomien()
}
}
}
Co dzieje się po kolei
- Każdy przełącznik ma własny listener i zmienia tylko „swoją” część podglądu. Nie ma przycisku „Zapisz” — tak działają ustawienia.
- Tryb ciemny zmienia tło karty i kolor trzech napisów. Oba stany muszą być obsłużone — wyłączenie przełącznika przywraca jasne kolory.
if (wlaczony) 20f else 14ftoifzwracający wartość — wybiera rozmiar w jednej linii.tvGodzina.isVisible = wlaczonychowa napis razem z miejscem, które zajmował (karta robi się niższa).swDzwiekma w XMLenabled="false". Włączają go dopiero powiadomienia, a ich wyłączenie dodatkowo wyłącza dźwięk.pokazStanPowiadomien()opisuje kombinację dwóch przełączników — dlatego wywołują ją oba listenery.
Sprawdź trzy rzeczy
- Włącz powiadomienia i dźwięk, potem wyłącz powiadomienia. Dźwięk sam się wyłączy i zrobi szary.
- Wyłącz „Pokaż godzinę” — karta się skurczy. Zmień w kodzie
isVisiblenatvGodzina.alpha = if (wlaczony) 1f else 0f. Godzina zniknie, ale miejsce zostanie. - Zamień jeden
MaterialSwitchw XML na<Switch>(i typ w kodzie). Zobaczysz, że wygląda inaczej niż pozostałe.
Zestawienie elementów
| Element | Typ | Co robi |
|---|---|---|
<com.google.android.material.materialswitch.MaterialSwitch> | kontrolka | Przełącznik Material 3. |
android:checked, android:enabled | atrybuty | Stan i aktywność na starcie. |
sw.isChecked | Boolean | Włączony czy wyłączony. |
sw.setOnCheckedChangeListener { _, wlaczony -> } | metoda | Kod przy każdej zmianie. |
view.isVisible | Boolean | Widoczność kontrolki (z miejscem). Import androidx.core.view.isVisible. |
view.alpha | Float | Przezroczystość — miejsce zostaje. |
if (…) a else b | wyrażenie | Wybór jednej z dwóch wartości. |
Zanim utkniesz
| Zapis / objaw | Problem |
|---|---|
<MaterialSwitch> bez pełnej nazwy | Awaria przy starcie: Error inflating class MaterialSwitch. Klasy spoza Androida wymagają pełnej ścieżki w XML. |
findViewById<Switch> dla MaterialSwitch | Awaria ClassCastException — to różne klasy. Typ w kodzie musi pasować do znacznika. |
| wyłączenie przełącznika nie przywraca wyglądu | Listener obsługuje tylko wlaczony == true. Zawsze pisz gałąź else. |
| przełącznik z przyciskiem „Zapisz” | Niezgodne z przyzwyczajeniami użytkowników — użyj CheckBox. |
| zależny przełącznik zostaje włączony | Przy wyłączaniu nadrzędnego trzeba też ustawić isChecked = false zależnemu, nie tylko isEnabled. |
Zadania
Każde zadanie wykonaj w nowym projekcie Empty Views Activity, używając MaterialSwitch. Cały kod pisz w onCreate. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.
Ikona żarówki (Vector Asset: lightbulb) i przełącznik „Światło”. Włączenie: ikona żółta, napis „Świeci”. Wyłączenie: ikona szara, napis „Zgaszone”.
pokaż wskazówkę
- Kolor ikony z kodu:
iv.setColorFilter(Color.parseColor("#FFC107")).
Pole hasła i przełącznik „Pokaż hasło”. Włączenie pokazuje wpisane znaki, wyłączenie ukrywa je z powrotem kropkami.
pokaż wskazówkę
etHaslo.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD(pokazane) albo… or InputType.TYPE_TEXT_VARIATION_PASSWORD(ukryte). Po zmianie przestaw kursor na koniec:etHaslo.setSelection(etHaslo.text.length).
Pięć przełączników: światło w salonie, w kuchni, w sypialni, ogrzewanie, alarm. Na górze „Włączone: 2 z 5” i szacowane zużycie energii (światło 10 W, ogrzewanie 2000 W, alarm 5 W). Włączenie alarmu wyłącza wszystkie światła.
pokaż wskazówkę
- Przełączniki w tablicy, liczenie w jednej funkcji. Wyłączanie świateł z kodu uruchomi ich listenery — dzięki temu licznik zaktualizuje się sam.
Pole liczbowe, przełącznik „km → mile” (wyłączony: mile → km) i wynik przeliczany przy każdym przełączeniu i po przycisku. Podpis przełącznika zmienia się na aktualny kierunek.
pokaż wskazówkę
- 1 mila = 1,609344 km. Podpis:
sw.text = if (wlaczony) "km → mile" else "mile → km".
Przełącznik główny „Tryb skupienia” i pod nim trzy zależne: „Wycisz powiadomienia”, „Ukryj gry”, „Szary ekran”. Zależne są aktywne tylko przy włączonym trybie. Włączenie trybu zaznacza wszystkie zależne, wyłączenie — odznacza i blokuje. „Szary ekran” zmienia tło całego ekranu na szare.
pokaż wskazówkę
- To ten sam wzór co powiadomienia i dźwięk w przykładzie, tylko dla trzech przełączników naraz.
Kalkulator abonamentu telefonicznego: cena bazowa 35 zł i przełączniki usług: internet bez limitu (+20), roaming UE (+0, ale wymaga internetu — bez niego jest nieaktywny), muzyka (+15), ubezpieczenie telefonu (+12), faktura papierowa (+5), rabat studencki (−20% od sumy, aktywny dopiero przy cenie co najmniej 60 zł). Lista włączonych usług i cena przeliczają się po każdym przełączeniu.
pokaż wskazówkę
- Rabat licz na końcu. Jeśli suma spadnie poniżej 60 zł, przełącznik rabatu trzeba wyłączyć i zablokować — uważaj, żeby zmiana z kodu nie wywoływała przeliczenia w nieskończoność (zmieniaj
isCheckedtylko wtedy, gdy rzeczywiście trzeba).
Co trzeba zapamiętać
- W projektach Material 3 używaj
com.google.android.material.materialswitch.MaterialSwitch. - W kodzie przełącznik działa jak
CheckBox:isCheckedisetOnCheckedChangeListener. - Przełącznik oznacza zmianę natychmiastową — bez przycisku „Zapisz”.
- W listenerze obsługuj oba stany: włączony i wyłączony.
- Zależny przełącznik:
isEnabledwedług nadrzędnego, a przy wyłączeniu nadrzędnego takżeisChecked = false.
Dokumentacja: Przełączniki w Androidzie, Material 3 — Switch.