Przejdź do treści
School IT / Przestrzeń do nauki

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.

XML LinearLayout orientation layout_weight padding / margin gravity strings.xml colors.xml 2 × 45 min
CEL LEKCJI

Czego się dziś nauczysz

  • Ułożysz kontrolki w pionie i w poziomie, zagnieżdżając jeden LinearLayout w drugim.
  • Dobierzesz wrap_content, match_parent i 0dp z wagą.
  • Odróżnisz padding od layout_margin i gravity od layout_gravity.
  • Podzielisz wiersz na równe części atrybutem layout_weight.
  • Przeniesiesz teksty do strings.xml, a kolory do colors.xml.
TEORIA

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:

AtrybutEfekt
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:

szkielet — pion z poziomym wierszem w środku
<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.

TEORIA

Szerokość i wysokość

Każda kontrolka musi mieć android:layout_width i android:layout_height. Wartości są trzy rodzaje:

WartośćZnaczenie
wrap_contentTyle, ile potrzebuje treść — napis „OK” da wąski przycisk.
match_parentTyle, ile ma rodzic — na całą szerokość ekranu.
liczba w dp, np. 120dpStały rozmiar. Używaj rzadko — tekst może się nie zmieścić.
12:30
wrap_content
match_parent
120dp — stała szerokość
trzy wartości layout_width
(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.

TEORIA

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.

12:30
bez odstępów
padding=”16dp”
tło rośnie, tekst odsuwa się od krawędzi
layout_margin=”16dp”
tło bez zmian, kontrolka odsunięta od sąsiadów
ten sam napis z różnymi odstępami

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ą.

TEORIA

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.

AtrybutCo wyrównujePrzykład
android:gravityTreść wewnątrz kontrolki (tekst w napisie, dzieci w układzie).Napis na całą szerokość z tekstem na środku.
android:layout_gravitySamą 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.

12:30
gravity=”center”
layout_gravity=”center”
layout_gravity=”end”
w pionowym LinearLayout

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.

TEORIA

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:

wiersz z trzema równymi kafelkami
<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>
12:30
wagi 1 : 1
A
B
wagi 2 : 1
A
B
wagi 1 : 1 : 1
A
Bardzo długi
C
wrap_content, bez wag
A
Bardzo długi
C
poziome wiersze z różnymi wagami

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.

TEORIA

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:

res/values/strings.xml — fragment
<string name="naglowek">Karta ucznia</string>
res/values/colors.xml — fragment
<color name="granat">#1A237E</color>
w układzie — odwołanie przez @
<TextView
    android:text="@string/naglowek"
    android:background="@color/granat" … />
ZapisZnaczenie
@string/naglowekTekst o nazwie naglowek z strings.xml.
@color/granatKolor o nazwie granat z colors.xml.
@+id/tvImieUtworzenie 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/….

PRZYKŁAD

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.

12:30
Karta ucznia
Anna Nowak
Klasa 2TP · rok szkolny 2026/2027
4,85
średnia
96%
frekwencja
120
punktów
Uwagi
Brak uwag. Aktywny udział w kole robotyki.
Oceny
Plan lekcji
telefon w pionie
12:30
Karta ucznia
Anna Nowak
4,85 średnia
96% frekwencja
120 punktów
Uwagi…
Oceny
Plan lekcji
po obróceniu — kafelki i przyciski nadal są równe

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

  1. Nowy projekt Empty Views Activity, nazwa KartaUcznia, Kotlin, API 24.
  2. Podmień activity_main.xml i strings.xml. Do istniejącego colors.xml dopisz trzy kolory (kolorów z szablonu nie usuwaj).
  3. W MainActivity.kt zostaw wersję kursową z lekcji 01 — sam setContentView.
app/src/main/res/
├── layout/
│   └── activity_main.xmlukład ekranu
└── values/
    ├── colors.xmlkolory — dopisujemy trzy
    └── strings.xmlteksty
res/values/strings.xml — pełny plik
<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>
res/values/colors.xml — dopisz wewnątrz <resources>
<color name="granat">#1A237E</color>
<color name="jasny">#E8EAF6</color>
<color name="bialy">#FFFFFF</color>
res/layout/activity_main.xml — pełny plik
<?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>
MainActivity.kt — pełny plik (bez zmian względem lekcji 01)
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 LinearLayout jest 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, a padding powiększa pasek.
  • Wiersz kafelków to poziomy LinearLayout. Kafelki mają szerokość 0dp i wagę 1 — każdy dostaje jedną trzecią. Odstęp między nimi daje layout_marginEnd na pierwszych dwóch.
  • Pole uwag ma w pionie 0dp i 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.xml znak \n — liczba i opis są w dwóch liniach.

Sprawdź trzy rzeczy

  1. Zmień wagę pierwszego kafelka na 2. Średnia zajmie połowę wiersza, pozostałe dwa — po ćwierć.
  2. Usuń z pola uwag wagę i ustaw wysokość wrap_content. Przyciski podjadą pod uwagi, a dół ekranu zostanie pusty.
  3. Zmień w colors.xml wartość koloru jasny na #FFF3E0. Zmienią się cztery kontrolki naraz — choć w układzie nie zmieniłeś ani jednej linii.
ELEMENTY WBUDOWANE

Zestawienie elementów

ElementTypCo robi
<LinearLayout>kontenerUkłada dzieci w jednej linii.
android:orientationatrybutvertical — pion, horizontal — poziom (domyślnie).
android:layout_weightatrybutUdział w podziale wolnego miejsca; zwykle z rozmiarem 0dp.
android:padding…atrybutOdstęp wewnątrz kontrolki.
android:layout_margin…atrybutOdstęp na zewnątrz kontrolki.
android:gravityatrybutWyrównanie treści wewnątrz kontrolki.
android:layout_gravityatrybutPołożenie kontrolki w rodzicu.
android:backgroundatrybutTło: kolor lub grafika.
android:textColor, textSize, textStyleatrybutyKolor, rozmiar i styl tekstu (bold, italic).
<string name="…">, @string/…zasóbTekst w strings.xml i odwołanie do niego.
<color name="…">, @color/…zasóbKolor w colors.xml i odwołanie do niego.
\nznakNowa linia w tekście.
CZĘSTE BŁĘDY

Zanim utkniesz

Zapis / objawProblem
trzy kafelki z match_parentPierwszy 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 wagiKontrolka ma zerowy rozmiar i znika.
brak android:orientationUkład jest poziomy — kontrolki ustawiają się w wierszu i uciekają za prawą krawędź.
@string/Naglowek przy nazwie naglowekBłą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 fitsSystemWindowsPadding zostaje zastąpiony. W głównym układzie użyj layout_margin.
komentarz <!-- --> wewnątrz znacznika, między atrybutamiBłąd składni XML. Komentarze tylko między elementami.
ZADANIA

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.

ZAD 1Flaga Polski i Włoch★☆☆

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.
ZAD 2Padding czy margin?★☆☆

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.
ZAD 3Kalkulator — sam wygląd★★☆

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 LinearLayout z layout_height="0dp" i wagą 1. Każdy przycisk: layout_width="0dp", wysokość match_parent, waga 1. Tekst do prawej: android:gravity="end".
ZAD 4Karta produktu po angielsku★★☆

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.
ZAD 5Tabela wyników ligi★★☆

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 LinearLayout z czterema TextView o wagach 1 : 3 : 1 : 1. Tło całego wiersza ustaw na LinearLayout, nie na każdym napisie.
ZAD 6Odtwórz ekran z telefonu★★★

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.
PODSUMOWANIE

Co trzeba zapamiętać

  • LinearLayout ukł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.
  • gravity wyrównuje treść kontrolki, layout_gravity — kontrolkę w rodzicu.
  • Teksty trzymaj w strings.xml, kolory w colors.xml, odwołuj się przez @string/… i @color/….

Dokumentacja: LinearLayout, Zasoby tekstowe, Zasoby kolorów.

Postęp zapisuje się w tej przeglądarce.