CheckBox i RadioButton

Jak budować formularze z polami wyboru: CheckBox pozwala zaznaczyć wiele opcji jednocześnie, RadioButton w grupie RadioGroup pozwala wybrać dokładnie jedną opcję.

CheckBox RadioButton RadioGroup isChecked INF.04
01

Czym się różnią?

Oba to kontrolki wyboru – różni je jedna fundamentalna zasada: CheckBox działa niezależnie od innych pól, RadioButton w grupie RadioGroup wymusza wybór tylko jednej opcji.

CheckBox – wiele wyborów

Każde pole działa niezależnie. Użytkownik może zaznaczyć zero, jedno lub kilka pól jednocześnie. Typowe zastosowanie: lista zgód, lista dodatków do zamówienia, ustawienia.

RadioButton – jeden wybór

Przyciski muszą być w grupie RadioGroup. Zaznaczenie jednego automatycznie odznacza pozostałe. Typowe zastosowanie: wybór płci, wybór rozmiaru, wybór metody płatności.

CechaCheckBoxRadioButton w RadioGroup
Ile opcji można wybrać?Dowolna liczba (0 lub więcej)Dokładnie jedna
Czy odznaczają się wzajemnie?❌ Nie✅ Tak – automatycznie
Wymaga grupy nadrzędnej?❌ Nie✅ Tak – RadioGroup
Jak sprawdzić stan?isChecked()isChecked() lub getCheckedRadioButtonId()

Oba dziedziczą po klasie CompoundButton, która dziedziczy po Button, a ten po TextView. Dlatego wszystkie metody znane z TextView – setText(), setTextColor(), setVisibility() – działają też tutaj.

View
TextView
Button
CompoundButton
CheckBox / RadioButton
02

CheckBox – pola wyboru

Atrybuty XML

res/layout/activity_main.xml – przykładowe CheckBoxy XML
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="24dp">

    <CheckBox
        android:id="@+id/cbSer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ser"
        android:textSize="16sp"
        android:checked="false"          <!-- domyślnie odznaczony -->
        android:layout_marginBottom="8dp" />

    <CheckBox
        android:id="@+id/cbSalami"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Salami"
        android:textSize="16sp"
        android:checked="true"           <!-- domyślnie zaznaczony -->
        android:layout_marginBottom="8dp" />

    <CheckBox
        android:id="@+id/cbPieczarki"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Pieczarki"
        android:textSize="16sp"
        android:layout_marginBottom="24dp" />

    <Button
        android:id="@+id/btnZamow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Zamów" />

    <TextView
        android:id="@+id/tvWynik"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:padding="12dp"
        android:layout_marginTop="16dp" />

</LinearLayout>

Odczyt stanu w kodzie

Stan CheckBoxa sprawdzamy metodą isChecked() która zwraca true (zaznaczony) lub false (odznaczony). Wywołujemy ją najczęściej po kliknięciu przycisku.

java/MainActivity.java – odczyt stanu CheckBox Java
// Sprawdź czy CheckBox jest zaznaczony
if (cbSer.isChecked()) {
    // zaznaczony
}

// Typowy wzorzec – zbieramy zaznaczone opcje
String dodatki = "";

if (cbSer.isChecked())       dodatki += "ser, ";
if (cbSalami.isChecked())    dodatki += "salami, ";
if (cbPieczarki.isChecked()) dodatki += "pieczarki, ";

if (dodatki.isEmpty()) {
    tvWynik.setText("Brak dodatków");
} else {
    // Usuń ostatni przecinek i spację
    tvWynik.setText("Dodatki: " + dodatki.substring(0, dodatki.length() - 2));
}
kotlin/MainActivity.kt – odczyt stanu CheckBox Kotlin
// Kotlin: isChecked jako właściwość
if (cbSer.isChecked) { /* zaznaczony */ }

// Zbieramy zaznaczone opcje do listy
val lista = mutableListOf<String>()

if (cbSer.isChecked)       lista.add("ser")
if (cbSalami.isChecked)    lista.add("salami")
if (cbPieczarki.isChecked) lista.add("pieczarki")

if (lista.isEmpty()) {
    tvWynik.text = "Brak dodatków"
} else {
    // joinToString łączy elementy listy separatorem
    tvWynik.text = "Dodatki: ${lista.joinToString(", ")}"
}

Nasłuch zmiany stanu – setOnCheckedChangeListener

Zamiast czekać na kliknięcie przycisku, możemy reagować na każdą zmianę stanu CheckBoxa w czasie rzeczywistym.

java/MainActivity.java – listener na zmianę stanu Java
// isChecked – aktualny stan po zmianie (true = zaznaczony)
cbSer.setOnCheckedChangeListener((buttonView, isChecked) -> {
    if (isChecked) {
        Toast.makeText(this, "Dodano ser", Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(this, "Usunięto ser", Toast.LENGTH_SHORT).show();
    }
});
kotlin/MainActivity.kt – listener na zmianę stanu Kotlin
cbSer.setOnCheckedChangeListener { buttonView, isChecked ->
    if (isChecked) {
        Toast.makeText(this, "Dodano ser", Toast.LENGTH_SHORT).show()
    } else {
        Toast.makeText(this, "Usunięto ser", Toast.LENGTH_SHORT).show()
    }
}
Parametry listenera

buttonView to referencja do CheckBoxa który zmienił stan – możesz jej użyć tak samo jak View v w onClick, np. buttonView.getId() żeby sprawdzić który CheckBox się zmienił. isChecked to nowy stan – true jeśli właśnie zaznaczono, false jeśli odznaczono.

Operacje na CheckBox w kodzie

java/MainActivity.java – operacje Java
// Zaznacz / odznacz programistycznie
cbSer.setChecked(true);
cbSer.setChecked(false);

// Przełącz stan (zaznaczony ↔ odznaczony)
cbSer.toggle();

// Dezaktywuj pole (szare, nieaktywne)
cbSer.setEnabled(false);

// Zmień tekst obok checkboxa
cbSer.setText("Ser (niedostępny)");
kotlin/MainActivity.kt – operacje Kotlin
// Zaznacz / odznacz programistycznie
cbSer.isChecked = true
cbSer.isChecked = false

// Przełącz stan
cbSer.toggle()

// Dezaktywuj pole
cbSer.isEnabled = false

// Zmień tekst
cbSer.text = "Ser (niedostępny)"

Pełny przykład – zamówienie pizzy

java/MainActivity.java – kompletna klasa Java
public class MainActivity extends AppCompatActivity {

    CheckBox cbSer, cbSalami, cbPieczarki;
    Button   btnZamow;
    TextView tvWynik;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initUI();
    }

    public void initUI() {
        cbSer       = findViewById(R.id.cbSer);
        cbSalami    = findViewById(R.id.cbSalami);
        cbPieczarki = findViewById(R.id.cbPieczarki);
        btnZamow    = findViewById(R.id.btnZamow);
        tvWynik     = findViewById(R.id.tvWynik);

        btnZamow.setOnClickListener(v -> {
            String dodatki = "";

            if (cbSer.isChecked())       dodatki += "ser, ";
            if (cbSalami.isChecked())    dodatki += "salami, ";
            if (cbPieczarki.isChecked()) dodatki += "pieczarki, ";

            if (dodatki.isEmpty()) {
                tvWynik.setText("Zamówiono: pizza bez dodatków");
            } else {
                tvWynik.setText("Zamówiono: " + dodatki.substring(0, dodatki.length() - 2));
            }
        });
    }
}
kotlin/MainActivity.kt – kompletna klasa Kotlin
class MainActivity : AppCompatActivity() {

    lateinit var cbSer       : CheckBox
    lateinit var cbSalami    : CheckBox
    lateinit var cbPieczarki : CheckBox
    lateinit var btnZamow    : Button
    lateinit var tvWynik     : TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initUI()
    }

    fun initUI() {
        cbSer       = findViewById(R.id.cbSer)
        cbSalami    = findViewById(R.id.cbSalami)
        cbPieczarki = findViewById(R.id.cbPieczarki)
        btnZamow    = findViewById(R.id.btnZamow)
        tvWynik     = findViewById(R.id.tvWynik)

        btnZamow.setOnClickListener {
            val lista = mutableListOf<String>()

            if (cbSer.isChecked)       lista.add("ser")
            if (cbSalami.isChecked)    lista.add("salami")
            if (cbPieczarki.isChecked) lista.add("pieczarki")

            val tekst = if (lista.isEmpty())
                "pizza bez dodatków"
            else
                lista.joinToString(", ")

            tvWynik.text = "Zamówiono: $tekst"
        }
    }
}
03

RadioButton i RadioGroup

RadioButton sam w sobie zachowuje się podobnie do CheckBoxa – możesz zaznaczać i odznaczać niezależnie. Dopiero umieszczenie wielu RadioButtonów w RadioGroup sprawia że zaznaczenie jednego automatycznie odznacza pozostałe.

RadioButton bez RadioGroup – pułapka

Jeśli umieścisz RadioButtony w zwykłym LinearLayout zamiast w RadioGroup, będą działać niezależnie jak CheckBoxy – można zaznaczyć kilka naraz. Zawsze umieszczaj RadioButtony w RadioGroup gdy chcesz wymusić wybór jednej opcji.

Atrybuty XML

res/layout/activity_main.xml – RadioGroup z RadioButtonami XML
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="24dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Wybierz rozmiar:"
        android:textSize="16sp"
        android:layout_marginBottom="8dp" />

    <!-- RadioGroup grupuje przyciski – zaznaczenie jednego odznacza resztę -->
    <RadioGroup
        android:id="@+id/rgRozmiar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"        <!-- vertical lub horizontal -->
        android:layout_marginBottom="24dp">

        <RadioButton
            android:id="@+id/rbMaly"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Mały (25 cm)"
            android:textSize="16sp" />

        <RadioButton
            android:id="@+id/rbSredni"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Średni (30 cm)"
            android:textSize="16sp"
            android:checked="true" />  <!-- domyślnie wybrany -->

        <RadioButton
            android:id="@+id/rbDuzy"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Duży (40 cm)"
            android:textSize="16sp" />

    </RadioGroup>

    <Button
        android:id="@+id/btnZamow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Zamów" />

    <TextView
        android:id="@+id/tvWynik"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        android:padding="12dp"
        android:layout_marginTop="16dp" />

</LinearLayout>

Odczyt zaznaczonej opcji

Mamy dwa sposoby sprawdzenia który RadioButton jest zaznaczony. Pierwszy – przez RadioGroup, drugi – bezpośrednio przez RadioButton.

java/MainActivity.java – odczyt zaznaczonej opcji Java
// Sposób 1: przez RadioGroup – pobieramy id zaznaczonego przycisku
int id = rgRozmiar.getCheckedRadioButtonId();

if (id == -1) {
    // Żaden przycisk nie jest zaznaczony
    Toast.makeText(this, "Wybierz rozmiar", Toast.LENGTH_SHORT).show();
    return;
}

switch (id) {
    case R.id.rbMaly:
        tvWynik.setText("Wybrałeś: mały (25 cm)");
        break;
    case R.id.rbSredni:
        tvWynik.setText("Wybrałeś: średni (30 cm)");
        break;
    case R.id.rbDuzy:
        tvWynik.setText("Wybrałeś: duży (40 cm)");
        break;
}

// Sposób 2: bezpośrednio przez RadioButton – isChecked()
if (rbMaly.isChecked())   tvWynik.setText("Mały");
if (rbSredni.isChecked()) tvWynik.setText("Średni");
if (rbDuzy.isChecked())   tvWynik.setText("Duży");
kotlin/MainActivity.kt – odczyt zaznaczonej opcji Kotlin
// Sposób 1: przez RadioGroup
val id = rgRozmiar.checkedRadioButtonId

if (id == -1) {
    Toast.makeText(this, "Wybierz rozmiar", Toast.LENGTH_SHORT).show()
    return
}

when (id) {
    R.id.rbMaly   -> tvWynik.text = "Wybrałeś: mały (25 cm)"
    R.id.rbSredni -> tvWynik.text = "Wybrałeś: średni (30 cm)"
    R.id.rbDuzy   -> tvWynik.text = "Wybrałeś: duży (40 cm)"
}

// Sposób 2: bezpośrednio przez RadioButton
if (rbMaly.isChecked)   tvWynik.text = "Mały"
if (rbSredni.isChecked) tvWynik.text = "Średni"
if (rbDuzy.isChecked)   tvWynik.text = "Duży"
getCheckedRadioButtonId() zwraca -1

Jeśli żaden RadioButton w grupie nie jest zaznaczony, getCheckedRadioButtonId() zwraca -1. Zawsze warto sprawdzić ten przypadek zanim przejdziesz do switch/when – chyba że w XML ustawiłeś jeden przycisk jako domyślnie zaznaczony przez android:checked="true".

Nasłuch zmiany wyboru

Listener rejestrujemy na RadioGroup, nie na poszczególnych RadioButtonach. Jeden listener obsługuje całą grupę.

java/MainActivity.java – listener na RadioGroup Java
rgRozmiar.setOnCheckedChangeListener((group, checkedId) -> {
    // checkedId – id właśnie zaznaczonego RadioButton
    switch (checkedId) {
        case R.id.rbMaly:
            Toast.makeText(this, "Mały – 25 cm", Toast.LENGTH_SHORT).show();
            break;
        case R.id.rbSredni:
            Toast.makeText(this, "Średni – 30 cm", Toast.LENGTH_SHORT).show();
            break;
        case R.id.rbDuzy:
            Toast.makeText(this, "Duży – 40 cm", Toast.LENGTH_SHORT).show();
            break;
    }
});
kotlin/MainActivity.kt – listener na RadioGroup Kotlin
rgRozmiar.setOnCheckedChangeListener { group, checkedId ->
    when (checkedId) {
        R.id.rbMaly   -> Toast.makeText(this, "Mały – 25 cm",   Toast.LENGTH_SHORT).show()
        R.id.rbSredni -> Toast.makeText(this, "Średni – 30 cm", Toast.LENGTH_SHORT).show()
        R.id.rbDuzy   -> Toast.makeText(this, "Duży – 40 cm",   Toast.LENGTH_SHORT).show()
    }
}

Operacje na RadioGroup i RadioButton w kodzie

java/MainActivity.java – operacje Java
// Zaznacz konkretny RadioButton przez id
rgRozmiar.check(R.id.rbSredni);

// Odznacz wszystkie (żaden nie zaznaczony)
rgRozmiar.clearCheck();

// Pobierz id aktualnie zaznaczonego
int zaznaczonyId = rgRozmiar.getCheckedRadioButtonId();

// Zmień orientację grupy na poziomą (można też w XML)
rgRozmiar.setOrientation(LinearLayout.HORIZONTAL);
kotlin/MainActivity.kt – operacje Kotlin
// Zaznacz konkretny RadioButton
rgRozmiar.check(R.id.rbSredni)

// Odznacz wszystkie
rgRozmiar.clearCheck()

// Pobierz id aktualnie zaznaczonego
val zaznaczonyId = rgRozmiar.checkedRadioButtonId

Pełny przykład – zamówienie rozmiaru

java/MainActivity.java – kompletna klasa Java
public class MainActivity extends AppCompatActivity {

    RadioGroup  rgRozmiar;
    RadioButton rbMaly, rbSredni, rbDuzy;
    Button      btnZamow;
    TextView    tvWynik;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initUI();
    }

    public void initUI() {
        rgRozmiar = findViewById(R.id.rgRozmiar);
        rbMaly    = findViewById(R.id.rbMaly);
        rbSredni  = findViewById(R.id.rbSredni);
        rbDuzy    = findViewById(R.id.rbDuzy);
        btnZamow  = findViewById(R.id.btnZamow);
        tvWynik   = findViewById(R.id.tvWynik);

        btnZamow.setOnClickListener(v -> {

            int id = rgRozmiar.getCheckedRadioButtonId();

            if (id == -1) {
                Toast.makeText(this, "Wybierz rozmiar pizzy!", Toast.LENGTH_SHORT).show();
                return;
            }

            String rozmiar = "";
            switch (id) {
                case R.id.rbMaly:   rozmiar = "Mały 25 cm";   break;
                case R.id.rbSredni: rozmiar = "Średni 30 cm"; break;
                case R.id.rbDuzy:   rozmiar = "Duży 40 cm";   break;
            }

            tvWynik.setText("Zamówiono: pizza " + rozmiar);
        });
    }
}
kotlin/MainActivity.kt – kompletna klasa Kotlin
class MainActivity : AppCompatActivity() {

    lateinit var rgRozmiar: RadioGroup
    lateinit var rbMaly   : RadioButton
    lateinit var rbSredni : RadioButton
    lateinit var rbDuzy   : RadioButton
    lateinit var btnZamow : Button
    lateinit var tvWynik  : TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initUI()
    }

    fun initUI() {
        rgRozmiar = findViewById(R.id.rgRozmiar)
        rbMaly    = findViewById(R.id.rbMaly)
        rbSredni  = findViewById(R.id.rbSredni)
        rbDuzy    = findViewById(R.id.rbDuzy)
        btnZamow  = findViewById(R.id.btnZamow)
        tvWynik   = findViewById(R.id.tvWynik)

        btnZamow.setOnClickListener {

            val id = rgRozmiar.checkedRadioButtonId

            if (id == -1) {
                Toast.makeText(this, "Wybierz rozmiar pizzy!", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            val rozmiar = when (id) {
                R.id.rbMaly   -> "Mały 25 cm"
                R.id.rbSredni -> "Średni 30 cm"
                R.id.rbDuzy   -> "Duży 40 cm"
                else          -> ""
            }

            tvWynik.text = "Zamówiono: pizza $rozmiar"
        }
    }
}
04

CheckBox vs RadioButton – jak wybrać?

Zasada jest prosta: zadaj sobie pytanie „Ile opcji może wybrać użytkownik?”

PytanieUżyjPrzykład
Może zaznaczyć zero lub więcej opcji?CheckBoxDodatki do pizzy, lista zgód, ustawienia powiadomień
Musi wybrać dokładnie jedną opcję?RadioButton + RadioGroupRozmiar, płeć, metoda dostawy, kategoria
Egzamin INF.04 – na co zwrócić uwagę

W zadaniach egzaminacyjnych RadioButtony zawsze muszą być w RadioGroup – bez niej można zaznaczyć kilka naraz i to jest błąd. Pamiętaj też o sprawdzeniu getCheckedRadioButtonId() == -1 gdy żaden nie jest domyślnie zaznaczony. Dla CheckBoxów standardowy wzorzec to sprawdzenie isChecked() każdego pola osobno po kliknięciu przycisku.

05

Podsumowanie

Operacja Java Kotlin
Znajdź CheckBox cb = findViewById(R.id.cb); cb = findViewById(R.id.cb)
Sprawdź stan cb.isChecked() cb.isChecked
Zaznacz / odznacz cb.setChecked(true/false) cb.isChecked = true/false
Przełącz stan cb.toggle() cb.toggle()
Listener zmiany stanu cb.setOnCheckedChangeListener((btn, isChecked) -> { }) cb.setOnCheckedChangeListener { btn, isChecked -> }
Który RadioButton zaznaczony? rg.getCheckedRadioButtonId() rg.checkedRadioButtonId
Zaznacz RadioButton przez id rg.check(R.id.rbNazwa) rg.check(R.id.rbNazwa)
Odznacz wszystkie w grupie rg.clearCheck() rg.clearCheck()
Listener zmiany w RadioGroup rg.setOnCheckedChangeListener((group, checkedId) -> { }) rg.setOnCheckedChangeListener { group, checkedId -> }
Zadanie

Zadanie dla uczniów

Formularz zamówienia pizzy

  • Stwórz formularz z RadioGroup do wyboru rozmiaru: Mała (20 zł), Średnia (30 zł), Duża (40 zł)
  • Dodaj trzy CheckBoxy z dodatkami: Ser (+3 zł), Salami (+5 zł), Pieczarki (+4 zł)
  • Dodaj przycisk „Oblicz cenę” i TextView na wynik
  • Po kliknięciu przycisku: sprawdź czy rozmiar jest wybrany (jeśli nie – Toast), zsumuj cenę bazową i zaznaczone dodatki, wyświetl w TextView: „Cena: X zł”
  • Zadeklaruj wszystkie widoki jako pola klasy, zainicjalizuj w initUI()

⭐ Bonus 1: dodaj setOnCheckedChangeListener na RadioGroup który na bieżąco aktualizuje TextView z ceną bazową

⭐⭐ Bonus 2: dodaj przycisk „Resetuj” który odznacza wszystkie CheckBoxy i czyści wybór w RadioGroup (clearCheck())

⭐⭐⭐ Bonus: połącz z poprzednim materiałem – po kliknięciu „Zamów” pokaż AlertDialog z potwierdzeniem zamówienia i ceną