Button i TextView

Jak działają dwa najważniejsze widoki Androida – co można z nimi robić w kodzie, jak reagować na kliknięcia i jak poprawnie obsługiwać wiele przycisków jednocześnie.

TextView Button setOnClickListener v.getId() INF.04
Język przykładów:
01

Widoki – co to jest?

Wszystko co widzisz na ekranie aplikacji Android to widok (ang. View). Każdy widok to obiekt klasy dziedziczącej po bazowej klasie View z pakietu android.view.

Interfejs użytkownika aplikacji Android buduje się z widoków układanych w hierarchię. Widoki definiuje się w pliku XML (co opisuje wygląd), a ich zachowaniem steruje się w kodzie Java lub Kotlin (co opisuje logikę).

Plik XML
wygląd i pozycja
Android kompiluje
tworzy klasę R
Kod Java/Kotlin
logika i zachowanie
Ekran aplikacji
widoczne dla użytkownika
Skąd pochodzi R.id.nazwaWidoku?

Kiedy kompilujesz projekt, Android Studio czyta wszystkie pliki XML i dla każdego atrybutu android:id="@+id/cokolwiek" generuje stałą liczbową w specjalnej klasie R. Dlatego R.id.tvWynik to po prostu liczba – unikalny numer identyfikujący widok. Metoda findViewById(R.id.tvWynik) przeszukuje układ i zwraca obiekt widoku o tym numerze. Bez android:id w XML – nie możemy odwołać się do widoku w kodzie.

WidokDo czego służyEdytowalny przez użytkownika?
TextViewWyświetlanie tekstu na ekranie❌ Nie – tylko do odczytu
ButtonPrzycisk reagujący na kliknięcie❌ Nie – ale rejestruje kliknięcia
EditTextPole do wpisywania tekstu przez użytkownika✅ Tak
02

TextView – pole tekstowe

TextView wyświetla tekst. Użytkownik nie może go edytować – tylko programista może zmieniać go w kodzie. Służy do etykiet, wyników obliczeń, komunikatów, nagłówków.

Atrybuty XML – definiowanie wyglądu

res/layout/activity_main.xml – przykładowy TextView XML
<TextView
    android:id="@+id/tvWynik"          <!-- ID do odwołania w kodzie -->
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Tekst domyślny"      <!-- tekst widoczny od razu po uruchomieniu -->
    android:textSize="20sp"             <!-- rozmiar – zawsze sp, nie px! -->
    android:textColor="#FFFFFF"         <!-- kolor – zapis szesnastkowy RGB -->
    android:textStyle="bold"            <!-- bold / italic / normal -->
    android:gravity="center"           <!-- wyrównanie tekstu wewnątrz widoku -->
    android:background="#2D2D2D"       <!-- kolor tła widoku -->
    android:padding="16dp"             <!-- odstęp wewnętrzny od krawędzi -->
    android:layout_margin="8dp"        <!-- odstęp zewnętrzny od sąsiednich widoków -->
    android:layout_marginBottom="24dp" <!-- można ustawić każdy bok osobno -->
    android:maxLines="3"              <!-- maksymalna liczba linii -->
    android:ellipsize="end" />         <!-- "..." gdy tekst jest za długi -->
sp vs dp – zasada której nie wolno łamać

Rozmiar tekstu zawsze w sp (scale-independent pixels) – uwzględnia ustawienia rozmiaru czcionki w systemie telefonu. Wszystkie pozostałe wymiary (padding, margin, width, height) w dp (density-independent pixels). Zapis px nigdy nie powinien się pojawiać – piksele nie skalują się między urządzeniami.

Zmiana tekstu w kodzie

W XML ustalamy tekst domyślny. W kodzie możemy go zmienić w dowolnym momencie – np. po kliknięciu przycisku, po obliczeniu wyniku, po pobraniu danych.

java/MainActivity.java – operacje na TextView Java
public class MainActivity extends AppCompatActivity {

    TextView tvWynik;   // pole klasy – dostępne we wszystkich metodach

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

    public void initUI() {
        tvWynik = findViewById(R.id.tvWynik);

        // Ustawienie nowego tekstu
        tvWynik.setText("Nowy tekst");

        // Odczytanie aktualnego tekstu
        String aktualnyTekst = tvWynik.getText().toString();

        // Składanie tekstu ze zmiennej
        int wynik = 42;
        tvWynik.setText("Wynik: " + wynik);

        // Ukrycie i pokazanie widoku
        tvWynik.setVisibility(View.GONE);      // ukryj i zwolnij miejsce
        tvWynik.setVisibility(View.INVISIBLE); // ukryj, ale zostaw puste miejsce
        tvWynik.setVisibility(View.VISIBLE);   // pokaż widok
    }
}
kotlin/MainActivity.kt – operacje na TextView Kotlin
class MainActivity : AppCompatActivity() {

    lateinit var tvWynik: TextView

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

    fun initUI() {
        tvWynik = findViewById(R.id.tvWynik)

        // Kotlin: właściwość .text zamiast setText() / getText()
        tvWynik.text = "Nowy tekst"

        // Odczytanie aktualnego tekstu
        val aktualnyTekst = tvWynik.text.toString()

        // String template – $ wstawia wartość zmiennej
        val wynik = 42
        tvWynik.text = "Wynik: $wynik"

        // Widoczność
        tvWynik.visibility = View.GONE
        tvWynik.visibility = View.VISIBLE
    }
}
Wartość visibilityWidoczny?Zajmuje miejsce?
View.VISIBLE✅ Tak✅ Tak
View.INVISIBLE❌ Nie✅ Tak – widok jest niewidoczny ale „trzyma” swoje miejsce
View.GONE❌ Nie❌ Nie – jakby widok nie istniał, inne widoki zajmują jego miejsce

Zmiana wyglądu w kodzie

Wszystkie atrybuty które ustawiłeś w XML możesz też ustawić lub zmienić w kodzie – na przykład w odpowiedzi na kliknięcie przycisku.

java/MainActivity.java – zmiana wyglądu TextView dynamicznie Java
// Zmiana koloru tekstu
tvWynik.setTextColor(Color.RED);                           // stała koloru
tvWynik.setTextColor(Color.parseColor("#FF5733"));        // hex string
tvWynik.setTextColor(getColor(R.color.colorPrimary));    // z zasobów colors.xml

// Zmiana rozmiaru tekstu (w sp)
tvWynik.setTextSize(24f);  // domyślnie w sp

// Zmiana koloru tła
tvWynik.setBackgroundColor(Color.parseColor("#2ECC71"));

// Pogrubienie / kursywa – Typeface
tvWynik.setTypeface(null, Typeface.BOLD);
tvWynik.setTypeface(null, Typeface.ITALIC);
tvWynik.setTypeface(null, Typeface.NORMAL);
kotlin/MainActivity.kt – zmiana wyglądu TextView dynamicznie Kotlin
// Zmiana koloru tekstu
tvWynik.setTextColor(Color.RED)
tvWynik.setTextColor(Color.parseColor("#FF5733"))
tvWynik.setTextColor(getColor(R.color.colorPrimary))

// Zmiana rozmiaru tekstu
tvWynik.setTextSize(24f)

// Zmiana koloru tła
tvWynik.setBackgroundColor(Color.parseColor("#2ECC71"))

// Pogrubienie / kursywa
tvWynik.setTypeface(null, Typeface.BOLD)
tvWynik.setTypeface(null, Typeface.NORMAL)
Praktyczne zastosowanie zmiany wyglądu

Zmiana koloru tekstu lub tła w kodzie przydaje się np. do sygnalizowania statusu: odpowiedź poprawna → tekst zielony, błędna → czerwony. Nie trzeba tworzyć dwóch osobnych widoków – wystarczy jeden TextView którego kolor zmieniamy w zależności od wyniku.

03

Button – przycisk

Button to klasa dziedzicząca po TextView – dlatego ma wszystkie atrybuty TextView (tekst, rozmiar, kolor) plus własne, związane z klikalnością.

Atrybuty XML

res/layout/activity_main.xml – przykładowy Button XML
<Button
    android:id="@+id/btnZapisz"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Zapisz dane"
    android:textSize="16sp"
    android:backgroundTint="#388BFD"     <!-- kolor tła Material Button -->
    android:textColor="#FFFFFF"
    android:paddingStart="24dp"
    android:paddingEnd="24dp"
    android:layout_marginTop="16dp"
    android:enabled="true" />            <!-- false = przycisk nieaktywny (szary) -->
Button dziedziczy po TextView

Ponieważ Button extends TextView, wszystkie metody które działają na TextView działają też na Button: setText(), setTextColor(), setVisibility(). Możesz zmieniać tekst przycisku w trakcie działania aplikacji – np. zmienić „Zatwierdź” na „Zmień” po pierwszym kliknięciu.

Jak działa kliknięcie – mechanizm

Gdy użytkownik dotyka ekranu, system Android generuje zdarzenie (ang. event). Zdarzenie wędruje przez hierarchię widoków aż do widoku który je obsługuje. Jeśli Button ma zarejestrowany nasłuchiwacz zdarzeń (ang. listener), Android wywołuje jego metodę onClick().

Użytkownik dotyka
ekran dotykowy
System Android
generuje zdarzenie
Button sprawdza
czy ma listener?
Wywołuje onClick()
nasz kod

Listener rejestrujemy metodą setOnClickListener(). Bez rejestracji listenera kliknięcie przycisku nic nie robi.

04

setOnClickListener – rejestracja nasłuchiwacza

setOnClickListener() to metoda która rejestruje obiekt nasłuchujący kliknięć. Przekazujesz jej implementację interfejsu View.OnClickListener – w praktyce dziś robi się to przez lambdę. To podstawowy i zalecany sposób obsługi kliknięć.

Jeden przycisk – pełny przykład

Tworzymy aplikację: użytkownik klika przycisk, w polu TextView pojawia się tekst. Pola widoków deklarujemy na poziomie klasy, a inicjalizację przeprowadzamy w metodzie initUI().

res/layout/activity_main.xml 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:gravity="center_horizontal"
    android:padding="24dp">

    <TextView
        android:id="@+id/tvWynik"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Naciśnij przycisk..."
        android:textSize="22sp"
        android:gravity="center"
        android:padding="20dp"
        android:layout_marginBottom="32dp" />

    <Button
        android:id="@+id/btnKliknij"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Kliknij mnie" />
    <!-- Brak android:onClick – obsługę kliknięcia ustawiamy w kodzie -->

</LinearLayout>
java/MainActivity.java Java
public class MainActivity extends AppCompatActivity {

    // Pola klasy – dostępne we wszystkich metodach, nie tylko w onCreate
    TextView tvWynik;
    Button   btnKliknij;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initUI();   // inicjalizacja widoków w osobnej metodzie – porządek w kodzie
    }

    public void initUI() {
        // Znajdź widoki w layoucie po ich android:id
        tvWynik    = findViewById(R.id.tvWynik);
        btnKliknij = findViewById(R.id.btnKliknij);

        // Zarejestruj listener – kod w lambdzie wykona się po kliknięciu
        btnKliknij.setOnClickListener(v -> {
            tvWynik.setText("Przycisk kliknięty!");
        });
    }
}
kotlin/MainActivity.kt Kotlin
class MainActivity : AppCompatActivity() {

    lateinit var tvWynik   : TextView
    lateinit var btnKliknij: Button

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

    fun initUI() {
        tvWynik    = findViewById(R.id.tvWynik)
        btnKliknij = findViewById(R.id.btnKliknij)

        // Kotlin: lambda bez nawiasów poza setOnClickListener
        btnKliknij.setOnClickListener {
            tvWynik.text = "Przycisk kliknięty!"
        }
    }
}
Dlaczego pola klasy i initUI()?

Deklarując TextView tvWynik; jako pole klasy, masz dostęp do tego widoku w każdej metodzie – nie tylko w onCreate(). Gdybyś zadeklarował go wewnątrz onCreate() jako zmienną lokalną, byłby niedostępny w innych metodach. Metoda initUI() to dobry zwyczaj: skupia całą inicjalizację widoków w jednym miejscu – kod jest przejrzystszy.

Wiele przycisków – jeden listener z v.getId()

Gdy mamy kilka przycisków, możemy stworzyć jeden listener i przypisać go do wszystkich. Parametr View v to referencja do widoku który kliknięto – wywołując v.getId() dostajemy jego identyfikator.

res/layout/activity_main.xml – przyciski bez android:onClick XML
<TextView android:id="@+id/tvWynik" android:textSize="24sp" android:text="Wynik: 0"
    android:layout_width="match_parent" android:layout_height="wrap_content"
    android:gravity="center" android:padding="16dp" android:layout_marginBottom="24dp" />

<Button android:id="@+id/btnDodaj"   android:text="Dodaj (+1)"
    android:layout_width="match_parent" android:layout_height="wrap_content" />
<Button android:id="@+id/btnOdejmij" android:text="Odejmij (−1)"
    android:layout_width="match_parent" android:layout_height="wrap_content" />
<Button android:id="@+id/btnCzysc"   android:text="Wyczyść"
    android:layout_width="match_parent" android:layout_height="wrap_content" />
java/MainActivity.java – jeden listener, switch na v.getId() Java
public class MainActivity extends AppCompatActivity {

    TextView tvWynik;
    Button   btnDodaj, btnOdejmij, btnCzysc;
    int      wynik = 0;  // pole klasy – zachowuje wartość między kliknięciami

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

    public void initUI() {
        tvWynik    = findViewById(R.id.tvWynik);
        btnDodaj   = findViewById(R.id.btnDodaj);
        btnOdejmij = findViewById(R.id.btnOdejmij);
        btnCzysc   = findViewById(R.id.btnCzysc);

        // Listener zapisany w zmiennej – możemy przypisać go do wielu przycisków
        View.OnClickListener listener = v -> {

            // v.getId() – identyfikator KLIKNIĘTEGO widoku
            switch (v.getId()) {
                case R.id.btnDodaj:    wynik++;    break;
                case R.id.btnOdejmij:  wynik--;    break;
                case R.id.btnCzysc:    wynik = 0; break;
            }
            tvWynik.setText("Wynik: " + wynik);
        };

        // Przypisz TEN SAM listener do wszystkich trzech przycisków
        btnDodaj  .setOnClickListener(listener);
        btnOdejmij.setOnClickListener(listener);
        btnCzysc  .setOnClickListener(listener);
    }
}
kotlin/MainActivity.kt – jeden listener, when na v.id Kotlin
class MainActivity : AppCompatActivity() {

    lateinit var tvWynik   : TextView
    lateinit var btnDodaj   : Button
    lateinit var btnOdejmij : Button
    lateinit var btnCzysc   : Button
    var wynik = 0

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

    fun initUI() {
        tvWynik    = findViewById(R.id.tvWynik)
        btnDodaj   = findViewById(R.id.btnDodaj)
        btnOdejmij = findViewById(R.id.btnOdejmij)
        btnCzysc   = findViewById(R.id.btnCzysc)

        val listener = View.OnClickListener { v ->
            when (v.id) {
                R.id.btnDodaj   -> wynik++
                R.id.btnOdejmij -> wynik--
                R.id.btnCzysc   -> wynik = 0
            }
            tvWynik.text = "Wynik: $wynik"
        }

        btnDodaj  .setOnClickListener(listener)
        btnOdejmij.setOnClickListener(listener)
        btnCzysc  .setOnClickListener(listener)
    }
}
05

android:onClick w XML – kiedy NIE używać

W starszych tutorialach i podręcznikach często pojawia się android:onClick="nazwaMetody" – atrybut który każe Androidowi wywołać metodę po kliknięciu. To wygodne skrócie, ale ma ważne ograniczenia których trzeba być świadomym.

Jak działa android:onClick

Wpisujesz w XML: android:onClick="kliknij". Android po kliknięciu szuka metody kliknij w bieżącej Activity. Metoda musi być public void kliknij(View v). Jeśli jej nie znajdzie – crash w czasie działania.

res/layout/activity_main.xml – android:onClick XML
<Button
    android:id="@+id/btnKliknij"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Kliknij"
    android:onClick="kliknij" />  <!-- szuka metody kliknij() w Activity -->
java/MainActivity.java – metoda musi spełniać 3 warunki Java
// Metoda MUSI być: public, void, z parametrem View v
public void kliknij(View v) {
    tvWynik.setText("Kliknięto!");
}
kotlin/MainActivity.kt – metoda musi spełniać 3 warunki Kotlin
// Metoda MUSI być widoczna z Javy (fun, nie private), zwracać Unit, mieć View
fun kliknij(v: View) {
    tvWynik.text = "Kliknięto!"
}

Poniżej zestawienie ograniczeń które sprawiają że android:onClick nie powinien być podstawowym wyborem:

Sytuacjaandroid:onClicksetOnClickListener
Używasz w Activity✅ Działa✅ Działa
Używasz w FragmentCrash – szuka metody w Activity, nie w Fragmencie✅ Działa
Przycisk tworzony dynamicznie w kodzie (nie z XML)❌ Niemożliwe – nie ma XML✅ Jedyna opcja
Błąd w nazwie metody❌ Crash w czasie działania – trudno znaleźć✅ Błąd kompilacji – łatwo znaleźć
Czytelność kodu⚠️ Logika „ukryta” w XML✅ Wszystko widoczne w kodzie
Refaktoryzacja (zmiana nazwy)⚠️ Trzeba pamiętać o zmianie w XML✅ IDE zmienia automatycznie
Wniosek – kiedy android:onClick jest OK

android:onClick jest szybki na szybkie prototypy i proste ćwiczenia w Activity. Jeśli piszesz „prawdziwy” kod, pracujesz z Fragmentami lub tworzysz widoki dynamicznie – zawsze używaj setOnClickListener. To bezpieczniejszy, bardziej elastyczny i zalecany przez Google sposób.

06

setOnLongClickListener – przytrzymanie

setOnLongClickListener działa tak samo jak setOnClickListener, ale reaguje na przytrzymanie widoku (ok. 1 sekundy). Używa się go do akcji „destrukcyjnych” lub wymagających potwierdzenia – np. usunięcie elementu, reset licznika.

Dlaczego metoda zwraca boolean?

onLongClick() musi zwrócić true lub false. Zwrócenie true oznacza: „obsłużyłem zdarzenie – koniec, nie wywołuj nic więcej”. Zwrócenie false oznacza: „obsłużyłem długie kliknięcie, ale potem wywołaj też normalne kliknięcie (onClick)”. W 99% przypadków zwracamy true.

java/MainActivity.java – oba listenery na jednym przycisku Java
public void initUI() {
    tvWynik    = findViewById(R.id.tvWynik);
    btnCzysc   = findViewById(R.id.btnCzysc);

    // Krótkie kliknięcie – normalny reset
    btnCzysc.setOnClickListener(v -> {
        wynik = 0;
        tvWynik.setText("Wynik: 0");
    });

    // Przytrzymanie – inny efekt, np. zmiana koloru + reset
    btnCzysc.setOnLongClickListener(v -> {
        wynik = 0;
        tvWynik.setText("Zresetowano!");
        tvWynik.setTextColor(Color.RED);
        return true;  // true = nie wywołuj onClick po tym
    });
}
kotlin/MainActivity.kt – oba listenery na jednym przycisku Kotlin
fun initUI() {
    tvWynik  = findViewById(R.id.tvWynik)
    btnCzysc = findViewById(R.id.btnCzysc)

    // Krótkie kliknięcie
    btnCzysc.setOnClickListener {
        wynik = 0
        tvWynik.text = "Wynik: 0"
    }

    // Przytrzymanie – ostatnia linia lambdy to zwracana wartość (true)
    btnCzysc.setOnLongClickListener {
        wynik = 0
        tvWynik.text = "Zresetowano!"
        tvWynik.setTextColor(Color.RED)
        true   // nie wywołuj onClick po tym
    }
}

LongClick na wielu przyciskach – identyczny wzorzec co onClick: jeden listener z switch / when na v.getId():

java/MainActivity.java – long click na wielu przyciskach Java
View.OnLongClickListener longListener = v -> {
    switch (v.getId()) {
        case R.id.btnDodaj:
            tvWynik.setText("Przytrzymano: DODAJ");
            break;
        case R.id.btnOdejmij:
            tvWynik.setText("Przytrzymano: ODEJMIJ");
            break;
    }
    return true;
};

btnDodaj  .setOnLongClickListener(longListener);
btnOdejmij.setOnLongClickListener(longListener);
kotlin/MainActivity.kt – long click na wielu przyciskach Kotlin
val longListener = View.OnLongClickListener { v ->
    when (v.id) {
        R.id.btnDodaj   -> tvWynik.text = "Przytrzymano: DODAJ"
        R.id.btnOdejmij -> tvWynik.text = "Przytrzymano: ODEJMIJ"
    }
    true
}

btnDodaj  .setOnLongClickListener(longListener)
btnOdejmij.setOnLongClickListener(longListener)
07

Podsumowanie

Operacja Java Kotlin
Znajdź widok tv = findViewById(R.id.tv); tv = findViewById(R.id.tv)
Ustaw tekst tv.setText("napis"); tv.text = "napis"
Odczytaj tekst tv.getText().toString() tv.text.toString()
Kolor tekstu tv.setTextColor(Color.RED); tv.setTextColor(Color.RED)
Kolor tła tv.setBackgroundColor(Color.parseColor("#hex")); tv.setBackgroundColor(Color.parseColor("#hex"))
Widoczność tv.setVisibility(View.GONE); tv.visibility = View.GONE
Listener (jeden) btn.setOnClickListener(v -> { }); btn.setOnClickListener { }
Który przycisk? switch(v.getId()) { case R.id.x: ... } when(v.id) { R.id.x -> ... }
Long click btn.setOnLongClickListener(v -> { return true; }); btn.setOnLongClickListener { true }
Zadanie

Zadanie dla uczniów

Prosta zgadywanka

  • Stwórz layout z TextView wyświetlającym pytanie: „Ile to 7 × 8?” i trzema przyciskami: 54, 56, 64
  • Zadeklaruj widoki jako pola klasy, zainicjalizuj w metodzie initUI()
  • Użyj setOnClickListener z jednym wspólnym listenerem i switch / when na v.getId()
  • Poprawna odpowiedź (56): TextView wyświetla „✅ Poprawnie!” i zmienia kolor tekstu na zielony (Color.parseColor("#2ECC71"))
  • Błędna odpowiedź: „❌ Błąd, spróbuj ponownie” i kolor tekstu na czerwony
  • Dodaj czwarty przycisk Reset który przywraca pierwotne pytanie i domyślny kolor tekstu

⭐ Bonus 1: do przycisku Reset dodaj setOnLongClickListener – przy przytrzymaniu tekst zmienia się na „Naprawdę resetujesz?” przez 2 sekundy

⭐⭐ Bonus 2: dodaj licznik prób – TextView wyświetla „Próba: X” po każdym kliknięciu odpowiedzi

⭐⭐⭐ Bonus Kotlin: przepisz całość używając właściwości zamiast setterów (.text, .visibility) i string templates ("Próba: $licznik")