school-it.pl // android + kotlin // rozdział 1 // lekcja 02
LinearLayout i zasoby
W lekcji 01 kontrolki ustawiały się jedna pod drugą — tak działa LinearLayout. Teraz nauczysz się nim sterować: układać kontrolki w wiersz, dzielić szerokość na równe części, odsuwać je od siebie i wyrównywać. Przeniesiesz też teksty i kolory do osobnych plików — zasobów — tak jak robi się to w prawdziwych aplikacjach.
Czego się dziś nauczysz
- Ułożysz kontrolki w pionie i w poziomie, zagnieżdżając jeden
LinearLayoutw drugim. - Dobierzesz
wrap_content,match_parenti0dpz wagą. - Odróżnisz
paddingodlayout_marginigravityodlayout_gravity. - Podzielisz wiersz na równe części atrybutem
layout_weight. - Przeniesiesz teksty do
strings.xml, a kolory docolors.xml.
Rodzic i dzieci
Plik układu to drzewo. Element, który zawiera inne, nazywamy rodzicem (kontenerem), a elementy w nim — dziećmi. LinearLayout układa swoje dzieci w jednej linii — w kierunku podanym w android:orientation:
| Atrybut | Efekt |
|---|---|
android:orientation="vertical" | Dzieci jedno pod drugim. |
android:orientation="horizontal" | Dzieci jedno obok drugiego. To wartość domyślna — gdy zapomnisz atrybutu, dostaniesz wiersz. |
Prawie każdy ekran to pionowy układ, w którym niektóre „piętra” są poziomymi wierszami. Wystarczy włożyć jeden LinearLayout w drugi:
<LinearLayout android:orientation="vertical" …> <!-- rodzic: pion -->
<TextView … /> <!-- piętro 1 -->
<LinearLayout android:orientation="horizontal" …> <!-- piętro 2: wiersz -->
<TextView … />
<TextView … />
</LinearLayout>
<TextView … /> <!-- piętro 3 -->
</LinearLayout>
Plik XML ma dokładnie jeden element główny. Każdy otwarty znacznik trzeba zamknąć: krótko />, gdy element nie ma dzieci, albo osobnym </LinearLayout>, gdy ma.
Szerokość i wysokość
Każda kontrolka musi mieć android:layout_width i android:layout_height. Wartości są trzy rodzaje:
| Wartość | Znaczenie |
|---|---|
wrap_content | Tyle, ile potrzebuje treść — napis „OK” da wąski przycisk. |
match_parent | Tyle, ile ma rodzic — na całą szerokość ekranu. |
liczba w dp, np. 120dp | Stały rozmiar. Używaj rzadko — tekst może się nie zmieścić. |
(tło pokazuje zajmowane miejsce)
ostatni napis nie mieści się w 120 dp i zawija się do drugiej linii
dp i sp
dp (density-independent pixel) to „piksel niezależny od gęstości ekranu”: 48 dp ma podobną fizyczną wielkość na każdym telefonie, choć na jednym to 96, a na innym 144 prawdziwych pikseli. sp działa tak samo, ale dodatkowo rośnie, gdy użytkownik powiększy czcionkę w ustawieniach. Zasada: tekst w sp, wszystko inne w dp.
Padding i margin
Oba atrybuty robią odstęp, ale w innym miejscu. Padding to odstęp wewnątrz kontrolki — między jej krawędzią a treścią; tło kontrolki go obejmuje. Margin to odstęp na zewnątrz — między kontrolką a sąsiadami; tło do niego nie sięga.
tło rośnie, tekst odsuwa się od krawędzi
tło bez zmian, kontrolka odsunięta od sąsiadów
padding jest częścią kontrolki (na niebieskim tle), margin — pustą przestrzenią wokół niej
Można podać odstęp ze wszystkich stron (android:padding, android:layout_margin) albo z jednej: …Top, …Bottom, …Start (lewa), …End (prawa), np. android:layout_marginTop="16dp". Zamiast Left/Right używamy Start/End — w językach pisanych od prawej (arabski) strony same się zamienią.
Wyrównanie: gravity i layout_gravity
Kolejna para podobnych nazw. Pomaga zasada: atrybuty z przedrostkiem layout_ to prośby do rodzica, a bez przedrostka — ustawienia samej kontrolki.
| Atrybut | Co wyrównuje | Przykład |
|---|---|---|
android:gravity | Treść wewnątrz kontrolki (tekst w napisie, dzieci w układzie). | Napis na całą szerokość z tekstem na środku. |
android:layout_gravity | Samą kontrolkę w rodzicu. | Wąski przycisk na środku albo przy prawej krawędzi. |
Wartości: center, center_horizontal, center_vertical, start, end, top, bottom. Można je łączyć kreską: end|bottom.
pierwszy napis ma match_parent i wyśrodkowany tekst; dwa kolejne mają wrap_content i są ustawione przez rodzica
layout_gravity działa w poprzek układu
W pionowym LinearLayout dziecko można przesunąć layout_gravity tylko w poziomie (lewo, środek, prawo). Pozycji w pionie nie zmieni — tę wyznacza kolejność w pliku. W poziomym układzie jest odwrotnie.
Podział miejsca — waga
Jak zrobić trzy kafelki o równej szerokości, niezależnie od długości tekstu i szerokości telefonu? Atrybutem android:layout_weight. Rodzic dzieli wolne miejsce proporcjonalnie do wag dzieci. Żeby liczyła się tylko waga, szerokość dzieci ustawiamy na 0dp:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="0dp" android:layout_weight="1" … />
<TextView android:layout_width="0dp" android:layout_weight="1" … />
<TextView android:layout_width="0dp" android:layout_weight="1" … />
</LinearLayout>
przy wagach 1:1:1 kafelki są równe mimo różnej długości tekstu; bez wag każdy jest tak szeroki, jak jego treść
W pionowym układzie działa to tak samo, tylko zerujemy wysokość: layout_height="0dp" + layout_weight="1" sprawia, że kontrolka zajmie całe wolne miejsce w pionie i zepchnie kolejne kontrolki na dół ekranu. Użyjemy tego w przykładzie.
Zasoby: teksty i kolory
W lekcji 01 teksty były wpisane wprost w XML, a Android Studio podkreślało je na żółto: Hardcoded string. Docelowo teksty trzymamy w pliku res/values/strings.xml, a kolory w res/values/colors.xml. Każdy zasób ma nazwę, przez którą się do niego odwołujemy:
<string name="naglowek">Karta ucznia</string>
<color name="granat">#1A237E</color>
<TextView
android:text="@string/naglowek"
android:background="@color/granat" … />
| Zapis | Znaczenie |
|---|---|
@string/naglowek | Tekst o nazwie naglowek z strings.xml. |
@color/granat | Kolor o nazwie granat z colors.xml. |
@+id/tvImie | Utworzenie identyfikatora (lekcja 01). |
getString(R.string.naglowek) | Ten sam tekst odczytany w kodzie Kotlin. |
Po co ta dodatkowa praca? Kolor użyty w dziesięciu miejscach zmieniasz w jednym. Tłumaczenie aplikacji na angielski to jeden dodatkowy plik strings.xml — układów nie ruszasz. Nazwy zasobów piszemy małymi literami, bez polskich znaków, słowa łączymy podkreślnikiem: imie_nazwisko.
Alt+Enter wyciąga tekst do zasobów
Kliknij podkreślony tekst w XML, naciśnij Alt+Enter → Extract string resource, wpisz nazwę. Android Studio samo dopisze go do strings.xml i zamieni na @string/….
Karta ucznia
Ekran zbudowany w całości w XML: granatowy nagłówek, dane ucznia, wiersz z trzema równymi kafelkami statystyk, pole uwag, które wypełnia wolne miejsce, i dwa równe przyciski przyklejone do dołu ekranu. Wszystkie teksty i kolory pochodzą z zasobów. W tej lekcji kod Kotlin się nie zmienia — przyciski jeszcze nic nie robią, obsługę kliknięć poznasz w lekcji 05.
średnia
frekwencja
punktów
wagi dopasowują układ do szerokości ekranu; na mniejszym ekranie w poziomie część treści może się nie zmieścić — przewijanie w lekcji 03
Przygotowanie projektu
- Nowy projekt Empty Views Activity, nazwa
KartaUcznia, Kotlin, API 24. - Podmień
activity_main.xmlistrings.xml. Do istniejącegocolors.xmldopisz trzy kolory (kolorów z szablonu nie usuwaj). - W
MainActivity.ktzostaw wersję kursową z lekcji 01 — samsetContentView.
├── layout/
│ └── activity_main.xmlukład ekranu
└── values/
├── colors.xmlkolory — dopisujemy trzy
└── strings.xmlteksty
<resources>
<string name="app_name">KartaUcznia</string>
<string name="naglowek">Karta ucznia</string>
<string name="imie_nazwisko">Anna Nowak</string>
<string name="klasa">Klasa 2TP · rok szkolny 2026/2027</string>
<!-- \n to przejście do nowej linii -->
<string name="srednia">4,85\nśrednia</string>
<!-- formatted="false": znak % jest zwykłym znakiem, a nie formatowaniem -->
<string name="frekwencja" formatted="false">96%\nfrekwencja</string>
<string name="punkty">120\npunktów</string>
<string name="uwagi_tytul">Uwagi</string>
<string name="uwagi">Brak uwag. Aktywny udział w kole robotyki.</string>
<string name="oceny">Oceny</string>
<string name="plan">Plan lekcji</string>
</resources>
<color name="granat">#1A237E</color>
<color name="jasny">#E8EAF6</color>
<color name="bialy">#FFFFFF</color>
<?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">
<!-- nagłówek: tło z zasobów, tekst na środku -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/granat"
android:gravity="center"
android:padding="16dp"
android:text="@string/naglowek"
android:textColor="@color/bialy"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/imie_nazwisko"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/klasa"
android:textSize="16sp" />
<!-- wiersz z trzema równymi kafelkami: szerokość 0dp + waga 1 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:background="@color/jasny"
android:gravity="center"
android:padding="12dp"
android:text="@string/srednia"
android:textSize="16sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:background="@color/jasny"
android:gravity="center"
android:padding="12dp"
android:text="@string/frekwencja"
android:textSize="16sp" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@color/jasny"
android:gravity="center"
android:padding="12dp"
android:text="@string/punkty"
android:textSize="16sp" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/uwagi_tytul"
android:textSize="16sp"
android:textStyle="bold" />
<!-- wysokość 0dp + waga 1: pole zajmuje całe wolne miejsce w pionie -->
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="4dp"
android:background="@color/jasny"
android:padding="12dp"
android:text="@string/uwagi"
android:textSize="16sp" />
<!-- dwa równe przyciski na dole ekranu -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:text="@string/oceny" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/plan" />
</LinearLayout>
</LinearLayout>
package pl.schoolit.kartaucznia
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
Co dzieje się po kolei
- Główny
LinearLayoutjest pionowy i ma sześć dzieci: nagłówek, imię, klasę, wiersz kafelków, tytuł „Uwagi”, pole uwag i wiersz przycisków. - Nagłówek ma
match_parent, więc tło zajmuje całą szerokość;gravity="center"ustawia tekst na środku tła, apaddingpowiększa pasek. - Wiersz kafelków to poziomy
LinearLayout. Kafelki mają szerokość0dpi wagę1— każdy dostaje jedną trzecią. Odstęp między nimi dajelayout_marginEndna pierwszych dwóch. - Pole uwag ma w pionie
0dpi wagę1. Jako jedyne w głównym układzie ma wagę, więc zabiera całą pozostałą wysokość — i dlatego przyciski lądują na dole ekranu. - Napisy kafelków mają w
strings.xmlznak\n— liczba i opis są w dwóch liniach.
Sprawdź trzy rzeczy
- Zmień wagę pierwszego kafelka na
2. Średnia zajmie połowę wiersza, pozostałe dwa — po ćwierć. - Usuń z pola uwag wagę i ustaw wysokość
wrap_content. Przyciski podjadą pod uwagi, a dół ekranu zostanie pusty. - Zmień w
colors.xmlwartość kolorujasnyna#FFF3E0. Zmienią się cztery kontrolki naraz — choć w układzie nie zmieniłeś ani jednej linii.
Zestawienie elementów
| Element | Typ | Co robi |
|---|---|---|
<LinearLayout> | kontener | Układa dzieci w jednej linii. |
android:orientation | atrybut | vertical — pion, horizontal — poziom (domyślnie). |
android:layout_weight | atrybut | Udział w podziale wolnego miejsca; zwykle z rozmiarem 0dp. |
android:padding… | atrybut | Odstęp wewnątrz kontrolki. |
android:layout_margin… | atrybut | Odstęp na zewnątrz kontrolki. |
android:gravity | atrybut | Wyrównanie treści wewnątrz kontrolki. |
android:layout_gravity | atrybut | Położenie kontrolki w rodzicu. |
android:background | atrybut | Tło: kolor lub grafika. |
android:textColor, textSize, textStyle | atrybuty | Kolor, rozmiar i styl tekstu (bold, italic). |
<string name="…">, @string/… | zasób | Tekst w strings.xml i odwołanie do niego. |
<color name="…">, @color/… | zasób | Kolor w colors.xml i odwołanie do niego. |
\n | znak | Nowa linia w tekście. |
Zanim utkniesz
| Zapis / objaw | Problem |
|---|---|
trzy kafelki z match_parent | Pierwszy zajmuje cały wiersz, pozostałych nie widać. Do podziału: 0dp + waga. |
waga przy layout_width="wrap_content" | Kafelki nierówne — waga dzieli tylko resztę miejsca po odjęciu szerokości treści. |
0dp bez wagi | Kontrolka ma zerowy rozmiar i znika. |
brak android:orientation | Układ jest poziomy — kontrolki ustawiają się w wierszu i uciekają za prawą krawędź. |
@string/Naglowek przy nazwie naglowek | Błąd budowania: resource string/Naglowek not found. Wielkość liter ma znaczenie. |
| nazwa zasobu z polskim znakiem lub spacją | Błąd budowania. Nazwy: małe litery, cyfry, podkreślnik. |
android:padding w elemencie z fitsSystemWindows | Padding zostaje zastąpiony. W głównym układzie użyj layout_margin. |
komentarz <!-- --> wewnątrz znacznika, między atrybutami | Błąd składni XML. Komentarze tylko między elementami. |
Zadania
Każde zadanie wykonaj w nowym projekcie Empty Views Activity. Wszystkie teksty i kolory umieszczaj w zasobach. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.
Górna połowa ekranu to flaga Polski (dwa poziome pasy: biały i czerwony), dolna — flaga Włoch (trzy pionowe pasy: zielony, biały, czerwony). Wszystko w LinearLayout z wagami, bez żadnych stałych rozmiarów w dp.
pokaż wskazówkę
- Pas flagi to pusty
<View>z tłem. Główny układ pionowy z dwoma dziećmi o wadze 1; pierwsze dziecko pionowe, drugie poziome.
Trzy napisy z tym samym żółtym tłem i tekstem „Jestem kontrolką”. Pierwszy bez odstępów, drugi z paddingiem 24dp, trzeci z marginesem 24dp. Pod spodem czwarty napis, w którym własnymi słowami opisujesz różnicę, jaką widzisz.
pokaż wskazówkę
- Gdzie kończy się żółte tło? To odpowiedź na pytanie.
Wygląd prostego kalkulatora: na górze pole wyniku „0” (duży tekst wyrównany do prawej), pod nim cztery wiersze po cztery przyciski (7 8 9 ÷ / 4 5 6 × / 1 2 3 − / C 0 = +). Przyciski w wierszu mają równe szerokości, a wiersze — równe wysokości i razem wypełniają resztę ekranu.
pokaż wskazówkę
- Każdy wiersz: poziomy
LinearLayoutzlayout_height="0dp"i wagą 1. Każdy przycisk:layout_width="0dp", wysokośćmatch_parent, waga 1. Tekst do prawej:android:gravity="end".
Karta produktu: nazwa, cena, opis, dwa przyciski „Do koszyka” i „Ulubione” (2 : 1). Następnie dodaj tłumaczenie: prawy przycisk na strings.xml → Open Translations Editor → kula ziemska → English, uzupełnij teksty. Zmień język emulatora na angielski i sprawdź, że aplikacja „sama” mówi po angielsku.
pokaż wskazówkę
- Edytor tłumaczeń tworzy plik
values-en/strings.xml. Teksty wpisane wprost w układ nie zostaną przetłumaczone — to najlepszy dowód, po co są zasoby.
Nagłówek „Tabela ligi szkolnej” i tabela z pięcioma drużynami: kolumny „Lp.”, „Drużyna”, „Mecze”, „Pkt”. Kolumna „Drużyna” jest trzy razy szersza od pozostałych, liczby są wyśrodkowane, a wiersz nagłówka tabeli ma ciemne tło i biały tekst. Co drugi wiersz ma jasnoszare tło.
pokaż wskazówkę
- Każdy wiersz to poziomy
LinearLayoutz czteremaTextViewo wagach 1 : 3 : 1 : 1. Tło całego wiersza ustaw naLinearLayout, nie na każdym napisie.
Zrób zrzut ekranu dowolnej aplikacji na swoim telefonie (np. odtwarzacz muzyki, prognoza pogody, ekran ustawień) i odtwórz jego układ wyłącznie z LinearLayout, TextView, Button i View. Ikony zastąp kolorowymi kwadratami. Ekran ma dobrze wyglądać także po obróceniu telefonu. Oddaj zrzut oryginału i swojego emulatora obok siebie.
pokaż wskazówkę
- Zanim zaczniesz pisać XML, narysuj na zrzucie prostokąty: które elementy są w pionie, które w wierszu. Każdy narysowany wiersz to osobny poziomy
LinearLayout.
Co trzeba zapamiętać
LinearLayoutukłada dzieci w pionie (vertical) albo poziomie (horizontal); układy można zagnieżdżać.- Rozmiar:
wrap_content— jak treść,match_parent— jak rodzic,0dp+layout_weight— część wolnego miejsca. padding— odstęp wewnątrz,layout_margin— na zewnątrz.gravitywyrównuje treść kontrolki,layout_gravity— kontrolkę w rodzicu.- Teksty trzymaj w
strings.xml, kolory wcolors.xml, odwołuj się przez@string/…i@color/….
Dokumentacja: LinearLayout, Zasoby tekstowe, Zasoby kolorów.