Przejdź do głównej treści
INF.04-02-26.01-SGStyczeń 2026 · 27 kroków · ok. 79 min · 32 kryteria z klucza

Rozwiązanie krok po kroku: Quiz górski konsolowy i mobilny (Java + Android)

Rozwiązujemy arkusz z quizem o górach w wersji Java + Android Studio: w konsoli klasa abstrakcyjna Pytanie i dziedzicząca po niej PytanieZamkniete z trzema odpowiedziami i metodą sprawdzającą odpowiedź, a do tego dwa testy z części III - próba utworzenia obiektu klasy abstrakcyjnej, która kończy się błędem kompilacji, i pytanie wczytane z klawiatury; w aplikacji mobilnej quiz ze zdjęciami z archiwum zad2.7z: pytanie, trzy odpowiedzi do wyboru i przycisk DALEJ, który liczy punkty i przechodzi do kolejnego pytania, a po ostatnim wraca do pierwszego. Kod aplikacji zapisujemy w repozytorium Git w dwóch migawkach.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się poleceniem z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Najpierw piszemy najprostszą wersję, której wymaga klucz, a w etapie „Dobre praktyki” poprawiamy to, czego klucz nie sprawdza.

Co widać w podglądzie. Wyniki programu konsolowego - także komunikat o błędzie z testu 1 - i wyniki poleceń Git pochodzą z prawdziwego uruchomienia kodu i poleceń z danego kroku (JDK 21, Git). Ekran telefonu to symulacja w przeglądarce zbudowana z pliku XML danego kroku; jej zachowanie odpowiada kodowi Javy z kroku - zaznacz odpowiedź i klikaj „DALEJ”. Każdy stan aplikacji zbudowaliśmy też w Android Studio (Gradle) i sprawdziliśmy na emulatorze Pixel 8.

Arkusz pozwala też na C++, C# albo Pythona w konsoli i na .NET MAUI w części mobilnej - ten wariant to jedna z możliwych dróg.

Jak liczą się punkty. Klucz ma 32 kryteria (R.1.1 - R.4.7), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 24 punkty - można stracić 8 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Przygotowanie

    Krok 1 · po tym etapie masz 0 z 32 kryteriów (pkt)

    Krok 1: Folder zdającego, podfoldery i archiwum zad2.7z

    ok. 3 min
    Polecenie z arkusza

    Utwórz folder i nazwij go numerem zdającego. W folderze utwórz podfoldery: konsolowa, mobilna, dokumentacja. Do zbudowania aplikacji zastosuj obrazy i treść pytań z archiwum zad2.7z zabezpieczonego hasłem ?Quiz5

    Na pulpicie zakładamy folder o nazwie z numerem PESEL, a w nim konsolowa, mobilna i dokumentacja. Archiwum zad2.7z rozpakowujemy 7-Zipem z hasłem ?Quiz5 - w środku folder zad2 z plikiem pytania.txt (trzy pytania, odpowiedzi, poprawna odpowiedź i nazwa obrazu) oraz zdjęciami zad1.jpg, zad2.jpg, zad3.jpg.

    Poprawne odpowiedzi z pytania.txt: „Na Wielkiej Raczy.”, „owczarek.”, „Tatry.” - w każdym pytaniu inna pozycja (druga, pierwsza, trzecia).

  2. Etap 2: Aplikacja konsolowa

    Kroki 2-7 · po tym etapie masz 13 z 32 kryteriów (pkt)

    Krok 2: Projekt konsola w Javie

    ok. 3 min
    Polecenie z arkusza

    Wykorzystując zasady programowania obiektowego zaprogramuj klasy do realizacji quizu o nazwach: Pytanie oraz PytanieZamkniete. Program wykonywany w konsoli. Zastosowany obiektowy język programowania zgodny z zainstalowanym na stanowisku egzaminacyjnym: C++ lub C#, lub Java, lub Python.

    Wybieramy Javę - tę samą, w której napiszemy aplikację mobilną. W IntelliJ IDEA: New Project → Java, nazwa konsola. Nazwy klas są narzucone przez arkusz - Pytanie i PytanieZamkniete, bez polskich znaków - więc pola i metody też nazwiemy po polsku, żeby kod był spójny.

    konsola/src/Main.java Java nowy plik
    public class Main {
        public static void main(String[] args) {
        }
    }
  3. Krok 3: Klasa abstrakcyjna Pytanie i jej pola protected

    Kryteria: , , , ,

    R.2.1 Program zawiera klasę Pytanie oraz klasę potomną PytanieZamkniete

    R.2.2 Klasa Pytanie jest abstrakcyjna (zawiera metodę abstrakcyjną lub słowo abstract, lub konstruktor niepubliczny, dziedziczy po klasie abc.ABC)

    R.2.3 Klasa Pytanie zawiera pola protected: dwa napisowe oraz jedno logiczne (w Python podkreślnik przed nazwą)

    R.1.4 Polskie lub angielskie, znaczące nazewnictwo pól. Wyjątkami od reguły są zmienne bufor, tmp, iteratory pętli itp. Kryterium nie jest spełnione, gdy nazwy zmiennych nic nie znaczą, np. x, fun, foo, tab, tablica

    R.1.5 Typy zmiennych pasują do problemu np. typ tekstowy dla treści pytania, odpowiedzi i nazwy obrazu, char dla udzielonej odpowiedzi, bool dla określenia czy odpowiedź jest prawdziwa (w przypadku języka Python typ wynika z przypisanych danych)

    ok. 3 min
    Polecenie z arkusza

    Klasa Pytanie, której obiekt nie może być powołany, zawiera: pola dostępne jedynie w klasie oraz w klasach potomnych, przechowujące: wartość tekstową z treścią pytania, nazwę pliku zawierającego zdjęcie do pytania, informację czy odpowiedź na pytanie jest poprawna, typu logicznego.

    „Obiekt nie może być powołany” w Javie zapisujemy słowem abstract: new Pytanie("Które to schronisko?", "zad1.jpg") nie skompiluje się (R.2.1, R.2.2).

    Pola „dostępne jedynie w klasie oraz w klasach potomnych” to protected (R.2.3): dwa napisy String - treść i nazwa pliku - oraz boolean czyPoprawna (R.1.5). Nazwy mówią, co przechowują (R.1.4).

    konsola/src/Pytanie.java Java nowy plik
    public abstract class Pytanie {
        protected String tresc;
        protected String plikObrazu;
        protected boolean czyPoprawna;
    }
  4. Krok 4: Konstruktor dwuargumentowy Pytanie

    Kryteria:

    R.2.5 Klasa Pytanie zawiera konstruktor dwuargumentowy, argumenty są przypisane odpowiednio do pól napisowych oraz pole logiczne jest ustawione na false

    ok. 2 min
    Polecenie z arkusza

    Konstruktor dwuargumentowy, którego argumentami są treść pytania i nazwa pliku dla pytania. Na podstawie argumentów ustawia wartości pól. Ustawia pole logiczne: odpowiedź na pytanie nie jest poprawna.

    Konstruktor przepisuje oba argumenty do pól (this. odróżnia pole od parametru o tej samej nazwie) i jawnie ustawia czyPoprawna = false (R.2.5). Pole boolean i tak ma domyślnie false, ale arkusz wymaga ustawienia w konstruktorze, a jawny zapis mówi czytelnikowi, że to decyzja, nie przypadek.

    Klasa abstrakcyjna może mieć konstruktor - wywołają go klasy potomne.

    konsola/src/Pytanie.java Java zmienione: 6 linii
    public abstract class Pytanie {
        protected String tresc;
        protected String plikObrazu;
        protected boolean czyPoprawna;
    
        public Pytanie(String tresc, String plikObrazu) {
            this.tresc = tresc;
            this.plikObrazu = plikObrazu;
            this.czyPoprawna = false;
        }
    }
  5. Krok 5: Metoda abstrakcyjna sprawdzOdpowiedz

    Kryteria: ,

    R.2.7 W klasie Pytanie zdefiniowana metoda abstrakcyjna typu logicznego (słowo kluczowe abstract lub Python: @abstractmethod i słowo pass) . Metoda nie ma implementacji oraz w klasie PytanieZamkniete istnieje metoda nadpisująca metodę abstrakcyjną z klasy Pytanie (w C# i Java obowiązkowo słowo override)

    R.1.3 Polskie lub angielskie, znaczące nazewnictwo metod

    ok. 2 min
    Polecenie z arkusza

    Metodę abstrakcyjną typu logicznego, sprawdzającą odpowiedź. Metoda ma jeden argument o typie znakowym, który przyjmuje wartości ‘A’ lub ‘B’ lub ‘C’ (odpowiedzi użytkownika na pytanie quizu). Metoda nie zawiera domyślnej implementacji.

    Metoda abstract ma sam nagłówek zakończony średnikiem - bez nawiasów klamrowych i bez ciała (R.2.7). Typ zwracany boolean, parametr char - jeden znak 'A', 'B' albo 'C' (R.1.5). Każda klasa potomna, która nie jest abstrakcyjna, musi ją zaimplementować - inaczej się nie skompiluje.

    konsola/src/Pytanie.java Java zmienione: 2 linie
    public abstract class Pytanie {
        protected String tresc;
        protected String plikObrazu;
        protected boolean czyPoprawna;
    
        public Pytanie(String tresc, String plikObrazu) {
            this.tresc = tresc;
            this.plikObrazu = plikObrazu;
            this.czyPoprawna = false;
        }
    
        public abstract boolean sprawdzOdpowiedz(char odpowiedz);
    }
  6. Krok 6: PytanieZamkniete: pola private i konstruktor przez super

    Kryteria: , , , ,

    R.2.1 Program zawiera klasę Pytanie oraz klasę potomną PytanieZamkniete

    R.2.4 Klasa PytanieZamkniete zawiera pola private: trzy napisowe oraz jedno typu znakowego (w Python dwa podkreślniki przed nazwą)

    R.2.6 Klasa PytanieZamkniete zawiera konstruktor sześcioargumentowy, który wywołuje konstruktor rodzica (np. super.__init__, :base(...), super(...) ) oraz przypisuje treści odpowiedzi i znak odpowiedzi prawidłowej (A, B lub C) do odpowiednich pól klasy

    R.1.1 Kod źródłowy zapisany w sposób czytelny: instrukcje w osobnych liniach, stosowane spacje pomiędzy operatorami, konsekwentnie stosowana wybrana konwencja dla nawiasów klamrowych

    R.1.2 Kod zapisany z wcięciami dla zagnieżdżeń bloków

    ok. 4 min
    Polecenie z arkusza

    Klasa PytanieZamkniete dziedzicząca po klasie Pytanie. Klasa zawiera: pola dostępne jedynie w klasie, niedostępne w klasach potomnych, przechowujące: treść odpowiedzi A, treść odpowiedzi B, treść odpowiedzi C, informację o poprawnej odpowiedzi (znak ‘A’ lub ‘B’ lub ‘C’). Konstruktor sześcioargumentowy, o argumentach: treść pytania, nazwa pliku graficznego dla pytania, treść odpowiedzi A, treść odpowiedzi B, treść odpowiedzi C, odpowiedź poprawna. Konstruktor wywołuje konstruktor klasy, po której dziedziczy. Przypisuje treści odpowiedzi oraz poprawną odpowiedź do pól klasy.

    extends Pytanie - klasa potomna (R.2.1). Pola niedostępne w klasach potomnych to private: trzy napisy z odpowiedziami i char poprawnaOdpowiedz (R.2.4).

    Konstruktor ma sześć parametrów. Pierwsza instrukcja to super(tresc, plikObrazu) - wywołanie konstruktora klasy Pytanie, który ustawi treść, plik i czyPoprawna. W Javie wywołanie super musi stać na początku konstruktora. Potem przepisujemy cztery własne pola (R.2.6).

    Uwaga: IntelliJ podkreśla teraz klasę na czerwono - nie implementuje jeszcze metody abstrakcyjnej. Naprawi to następny krok.

    Typowe błędy

    Przypisanie this.tresc = tresc; w klasie potomnej zamiast super(tresc, plikObrazu) - kompilator żąda wywołania konstruktora rodzica, bo Pytanie nie ma konstruktora bezparametrowego (R.2.6).

    konsola/src/PytanieZamkniete.java Java nowy plik
    public class PytanieZamkniete extends Pytanie {
        private String odpowiedzA;
        private String odpowiedzB;
        private String odpowiedzC;
        private char poprawnaOdpowiedz;
    
        public PytanieZamkniete(String tresc, String plikObrazu, String odpowiedzA, String odpowiedzB,
                                String odpowiedzC, char poprawnaOdpowiedz) {
            super(tresc, plikObrazu);
            this.odpowiedzA = odpowiedzA;
            this.odpowiedzB = odpowiedzB;
            this.odpowiedzC = odpowiedzC;
            this.poprawnaOdpowiedz = poprawnaOdpowiedz;
        }
    }
  7. Krok 7: Implementacja sprawdzOdpowiedz z @Override

    Kryteria: ,

    R.2.7 W klasie Pytanie zdefiniowana metoda abstrakcyjna typu logicznego (słowo kluczowe abstract lub Python: @abstractmethod i słowo pass) . Metoda nie ma implementacji oraz w klasie PytanieZamkniete istnieje metoda nadpisująca metodę abstrakcyjną z klasy Pytanie (w C# i Java obowiązkowo słowo override)

    R.2.8 W klasie PytanieZamkniete jest zaimplementowana metoda, która sprawdza czy odpowiedź z argumentu zgadza się z poprawną, jeśli tak, przypisuje zmiennej logicznej z klasy Pytanie wartość true; zwracana jest wartość pola logicznego

    ok. 3 min
    Polecenie z arkusza

    Implementację metody abstrakcyjnej sprawdzającej odpowiedź, która: sprawdza czy odpowiedź jest poprawna i w zależności od wyniku, przypisuje odpowiednią wartość do pola logicznego. Zwraca wartość pola logicznego.

    @Override oznacza, że metoda nadpisuje metodę z klasy bazowej - kompilator sprawdzi, czy nagłówek naprawdę do niej pasuje (R.2.7).

    Wynik porównania odpowiedz == poprawnaOdpowiedz to od razu true albo false - przypisujemy go do odziedziczonego pola czyPoprawna (dostępnego, bo jest protected) i zwracamy (R.2.8). Znaki char porównujemy operatorem ==; dla napisów String trzeba by użyć equals.

    konsola/src/PytanieZamkniete.java Java zmienione: 6 linii
    public class PytanieZamkniete extends Pytanie {
        private String odpowiedzA;
        private String odpowiedzB;
        private String odpowiedzC;
        private char poprawnaOdpowiedz;
    
        public PytanieZamkniete(String tresc, String plikObrazu, String odpowiedzA, String odpowiedzB,
                                String odpowiedzC, char poprawnaOdpowiedz) {
            super(tresc, plikObrazu);
            this.odpowiedzA = odpowiedzA;
            this.odpowiedzB = odpowiedzB;
            this.odpowiedzC = odpowiedzC;
            this.poprawnaOdpowiedz = poprawnaOdpowiedz;
        }
    
        @Override
        public boolean sprawdzOdpowiedz(char odpowiedz) {
            czyPoprawna = odpowiedz == poprawnaOdpowiedz;
            return czyPoprawna;
        }
    }
  8. Etap 3: Testy aplikacji konsolowej

    Kroki 8-11 · po tym etapie masz 17 z 32 kryteriów (pkt)

    Krok 8: Test 1: próba utworzenia obiektu klasy Pytanie

    Kryteria: ,

    R.4.1 W aplikacji konsolowej jest wykonany test inicjacji obiektu klasy Pytanie - test nie powiódł się (kod nie skompilował się) , co jest udokumentowane na zrzucie ekranu

    R.1.6 Podjęta próba uruchomienia kodu, co jest udokumentowane zrzutem ekranowym przedstawiającym uruchomiony program lub jego kompilację

    ok. 2 min
    Polecenie z arkusza

    Sprawdź działanie klas zaimplementowanych w I części egzaminu. Zweryfikuj możliwość utworzenia obiektu klasy Pytanie. Po próbie uruchomienia programu wykonaj zrzut ekranu z otrzymanym błędem i zapisz pod nazwą test1.png w folderze dokumentacja.

    W main próbujemy utworzyć obiekt klasy abstrakcyjnej. Program nie skompiluje się - i o to w tym teście chodzi: klasa „której obiekt nie może być powołany” naprawdę go nie pozwala utworzyć. Podgląd to prawdziwy komunikat kompilatora: „Pytanie is abstract; cannot be instantiated”.

    Zrzut całego ekranu z IntelliJ z tym błędem zapisujemy jako test1.png w folderze dokumentacja (R.4.1, R.1.6).

    konsola/src/Main.java Java zmienione: 1 linia
    public class Main {
        public static void main(String[] args) {
            Pytanie pytanie = new Pytanie("Które to schronisko?", "zad1.jpg");
        }
    }
    Wynik programu po tym kroku
    > javac -encoding UTF-8 src\*.java -d out
    src\Main.java:3: error: Pytanie is abstract; cannot be instantiated
            Pytanie pytanie = new Pytanie("Które to schronisko?", "zad1.jpg");
                              ^
    1 error
  9. Krok 9: Test 2: dane pytania z klawiatury

    Kryteria: ,

    R.4.2 W aplikacji konsolowej jest wykonany test obiektu klasy PytanieZamkniete : obiekt inicjowano danymi wczytanymi z klawiatury, udzielono odpowiedzi, program wypisał czy odpowiedź jest prawidłowa (metoda sprawdzająca poprawność) co jest udokumentowane na zrzucie ekranu

    R.1.7 Program nawiązuje zrozumiałą komunikację z użytkownikiem. Wystarczy, że wyświetla dane (quiz) lub informację o poprawności udzielonej odpowiedzi w dowolnej aplikacji. Jeżeli kod nie uruchamia się z powodu błędów kompilacji - sprawdzić w kodzie aplikacji

    ok. 4 min
    Polecenie z arkusza

    Następnie umieść ten kod w komentarzu. W programie głównym zainicjuj obiekt klasy PytanieZamkniete, dane do konstruktora wczytaj z klawiatury.

    Kod testu 1 zamieniamy w komentarz - zostaje w pliku jako ślad testu, a program znów się kompiluje.

    Sześć danych do konstruktora wczytujemy z klawiatury, każde po opisie (R.1.7). Napisy - całym wierszem nextLine() (odpowiedzi zawierają spacje). Poprawna odpowiedź to jeden znak: charAt(0) bierze pierwszy znak wpisanego wiersza. Z tych danych tworzymy obiekt PytanieZamkniete (R.4.2).

    konsola/src/Main.java Java zmienione: 20 linii
    import java.util.Scanner;
    
    public class Main {
        public static void main(String[] args) {
            // Test 1: obiektu klasy abstrakcyjnej nie można utworzyć - błąd kompilacji (zrzut test1.png)
            // Pytanie pytanie = new Pytanie("Które to schronisko?", "zad1.jpg");
    
            Scanner scanner = new Scanner(System.in);
            System.out.print("Treść pytania: ");
            String tresc = scanner.nextLine();
            System.out.print("Nazwa pliku obrazu: ");
            String plikObrazu = scanner.nextLine();
            System.out.print("Odpowiedź A: ");
            String odpowiedzA = scanner.nextLine();
            System.out.print("Odpowiedź B: ");
            String odpowiedzB = scanner.nextLine();
            System.out.print("Odpowiedź C: ");
            String odpowiedzC = scanner.nextLine();
            System.out.print("Poprawna odpowiedź (A, B lub C): ");
            char poprawna = scanner.nextLine().charAt(0);
    
            PytanieZamkniete pytanie = new PytanieZamkniete(tresc, plikObrazu, odpowiedzA, odpowiedzB, odpowiedzC, poprawna);
        }
    }
    Wynik programu po tym kroku
    > java Main
    Treść pytania: Które to schronisko?
    Nazwa pliku obrazu: zad1.jpg
    Odpowiedź A: Na Rysiance.
    Odpowiedź B: Na Wielkiej Raczy.
    Odpowiedź C: Na Wielkiej Rycerzowej.
    Poprawna odpowiedź (A, B lub C): B
  10. Krok 10: Test 2: odpowiedź z klawiatury i wynik

    Kryteria:

    R.4.2 W aplikacji konsolowej jest wykonany test obiektu klasy PytanieZamkniete : obiekt inicjowano danymi wczytanymi z klawiatury, udzielono odpowiedzi, program wypisał czy odpowiedź jest prawidłowa (metoda sprawdzająca poprawność) co jest udokumentowane na zrzucie ekranu

    ok. 3 min
    Polecenie z arkusza

    Wczytaj z klawiatury odpowiedź na pytanie. Sprawdź za pomocą metody poprawność odpowiedzi i wyświetl na ekranie tekst „Odpowiedź prawidłowa” lub „Odpowiedź nieprawidłowa”. Wykonaj zrzut ekranu o nazwie test2.png.

    Odpowiedź użytkownika to znowu jeden znak. Metoda sprawdzOdpowiedz zwraca boolean, więc wywołujemy ją wprost w warunku if i wypisujemy jeden z dwóch tekstów z arkusza (R.4.2).

    Podgląd to dwa prawdziwe uruchomienia z pierwszym pytaniem z pytania.txt: odpowiedź B (prawidłowa) i A (nieprawidłowa). Zrzut z wynikiem - test2.png w folderze dokumentacja.

    konsola/src/Main.java Java zmienione: 8 linii
    import java.util.Scanner;
    
    public class Main {
        public static void main(String[] args) {
            // Test 1: obiektu klasy abstrakcyjnej nie można utworzyć - błąd kompilacji (zrzut test1.png)
            // Pytanie pytanie = new Pytanie("Które to schronisko?", "zad1.jpg");
    
            Scanner scanner = new Scanner(System.in);
            System.out.print("Treść pytania: ");
            String tresc = scanner.nextLine();
            System.out.print("Nazwa pliku obrazu: ");
            String plikObrazu = scanner.nextLine();
            System.out.print("Odpowiedź A: ");
            String odpowiedzA = scanner.nextLine();
            System.out.print("Odpowiedź B: ");
            String odpowiedzB = scanner.nextLine();
            System.out.print("Odpowiedź C: ");
            String odpowiedzC = scanner.nextLine();
            System.out.print("Poprawna odpowiedź (A, B lub C): ");
            char poprawna = scanner.nextLine().charAt(0);
    
            PytanieZamkniete pytanie = new PytanieZamkniete(tresc, plikObrazu, odpowiedzA, odpowiedzB, odpowiedzC, poprawna);
    
            System.out.print("Twoja odpowiedź: ");
            char odpowiedz = scanner.nextLine().charAt(0);
            if (pytanie.sprawdzOdpowiedz(odpowiedz)) {
                System.out.println("Odpowiedź prawidłowa");
            } else {
                System.out.println("Odpowiedź nieprawidłowa");
            }
        }
    }
    Wynik programu po tym kroku
    > java Main
    Treść pytania: Które to schronisko?
    Nazwa pliku obrazu: zad1.jpg
    Odpowiedź A: Na Rysiance.
    Odpowiedź B: Na Wielkiej Raczy.
    Odpowiedź C: Na Wielkiej Rycerzowej.
    Poprawna odpowiedź (A, B lub C): B
    Twoja odpowiedź: B
    Odpowiedź prawidłowa
    
    > java Main
    Treść pytania: Które to schronisko?
    Nazwa pliku obrazu: zad1.jpg
    Odpowiedź A: Na Rysiance.
    Odpowiedź B: Na Wielkiej Raczy.
    Odpowiedź C: Na Wielkiej Rycerzowej.
    Poprawna odpowiedź (A, B lub C): B
    Twoja odpowiedź: A
    Odpowiedź nieprawidłowa
  11. Krok 11: Folder konsolowa: archiwum i źródła

    ok. 2 min
    Polecenie z arkusza

    Kod aplikacji przygotuj do nagrania na płytę. W folderze konsolowa powinno znaleźć się archiwum całego projektu o nazwie konsola.zip, skopiowany z projektu plik z kodem źródłowym programu oraz plik wykonywalny, jeżeli istnieje.

    Folder projektu pakujemy do konsola.zip, obok kopiujemy Pytanie.java, PytanieZamkniete.java i Main.java (R.4.7). Java nie tworzy pliku .exe - plik wykonywalny pomijamy.

    Wynik programu bez zmian - taki jak po kroku 10.

  12. Etap 4: Aplikacja mobilna

    Kroki 12-16 · po tym etapie masz 20 z 32 kryteriów (pkt)

    Krok 12: Nowy projekt Quiz i zdjęcia w drawable

    ok. 4 min
    Polecenie z arkusza

    Za pomocą środowiska programistycznego dostępnego na stanowisku egzaminacyjnym wykonaj aplikację mobilną quizu dotyczącego gór.

    New Project → szablon Empty Views Activity (z widokami w XML). Nazwa Quiz, pakiet com.example.quiz, język Java. Zdjęcia zad1.jpg - zad3.jpg kopiujemy do app/src/main/res/drawable; w kodzie będą dostępne jako R.drawable.zad1 itd.

    Zdjęcia z archiwum mają 4032 × 3024 pikseli (do 7 MB każde). Aplikacja z nimi działa, ale na emulatorze uruchamiała się 13-15 sekund, bo Android dekoduje całe zdjęcie, choć ekran pokazuje je w małej ramce. Po zmniejszeniu do 1200 px (np. w Paincie: Zmień rozmiar) start trwał ok. 7 sekund. Na egzaminie można wkleić oryginały - zmniejszenie to oszczędność, nie wymóg.

    Quiz/app/src/main/java/com/example/quiz/MainActivity.java Java nowy plik
    package com.example.quiz;
    
    import android.os.Bundle;
    
    import androidx.activity.EdgeToEdge;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.graphics.Insets;
    import androidx.core.view.ViewCompat;
    import androidx.core.view.WindowInsetsCompat;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            EdgeToEdge.enable(this);
            setContentView(R.layout.activity_main);
            ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
                Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
                v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
                return insets;
            });
        }
    }
    Quiz/app/src/main/res/layout/activity_main.xml XML nowy plik
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  13. Krok 13: Pionowy LinearLayout z tłem #2E7CB8

    Kryteria: ,

    R.3.1 Zastosowany język znaczników XML/XAML lub inny do opisu interfejsu użytkownika oraz zdefiniowana przynajmniej jedna kontrolka

    R.3.3 Kolor tła okna lub rozkładu #2E7CB8, rozmiar czcionki z treścią pytania jest większy niż pozostałe treści, w jednym momencie można wybrać tylko jedno pole radio

    ok. 2 min
    Polecenie z arkusza

    Interfejs użytkownika zapisany za pomocą języka znaczników wspieranego w danym środowisku (np. XAML, XML). Zastosowany dowolny rozkład pozwalający na rozmieszczenie elementów zgodnie z obrazami 1 lub 2. Tło okna lub rozkładu #2E7CB8.

    W widoku Code zamieniamy ConstraintLayout na pionowy LinearLayout (R.3.1) z tłem #2E7CB8 (R.3.3) i zostawiamy android:id="@+id/main" (używa go kod szablonu). Elementy z obrazu 1 stoją jeden pod drugim - pionowy układ wystarczy.

    Quiz/app/src/main/res/layout/activity_main.xml XML zmienione: 4 linie
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#2E7CB8"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
    </LinearLayout>
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  14. Krok 14: Zdjęcie zad1.jpg i treść pytania na środku

    Kryteria: ,

    R.3.2 Zdefiniowane kontrolki: obraz z pliku zad1.jpg , pole tekstowe z treścią pytania: „Które to schronisko?” trzy pola radio z odpowiedziami, oraz przycisk DALEJ

    R.3.3 Kolor tła okna lub rozkładu #2E7CB8, rozmiar czcionki z treścią pytania jest większy niż pozostałe treści, w jednym momencie można wybrać tylko jedno pole radio

    ok. 3 min
    Polecenie z arkusza

    Obraz z wartością początkową zad1.jpg. Pole tekstowe z treścią pytania: „Które to schronisko?”. Biały kolor czcionki dla elementów. Rozmiar czcionki z treścią pytania większy niż pozostałe treści. Wyśrodkowane w poziomie obraz, treść pytania i przycisk.

    • Obraz ma szerokość ekranu i wysokość 250dp. ImageView domyślnie skaluje zdjęcie tak, by zmieściło się w ramce w całości, i ustawia je na środku ramki - stąd wyśrodkowanie w poziomie, także dla pionowego zdjęcia psa (R.3.2).
    • Pytanie ma szerokość treści, a layout_gravity="center_horizontal" stawia je na środku układu. Biały tekst, 24sp - pozostałe teksty będą mniejsze (R.3.2, R.3.3).

    Oba elementy mają identyfikatory: kod będzie podmieniał zdjęcie i treść przy kolejnych pytaniach.

    Quiz/app/src/main/res/layout/activity_main.xml XML zmienione: 18 linii
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#2E7CB8"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <ImageView
            android:id="@+id/questionImage"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:layout_marginTop="40dp"
            android:contentDescription="Zdjęcie do pytania"
            android:src="@drawable/zad1" />
    
        <TextView
            android:id="@+id/questionText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="8dp"
            android:text="Które to schronisko?"
            android:textColor="#FFFFFF"
            android:textSize="24sp" />
    
    </LinearLayout>
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  15. Krok 15: Trzy odpowiedzi w RadioGroup

    Kryteria: ,

    R.3.2 Zdefiniowane kontrolki: obraz z pliku zad1.jpg , pole tekstowe z treścią pytania: „Które to schronisko?” trzy pola radio z odpowiedziami, oraz przycisk DALEJ

    R.3.3 Kolor tła okna lub rozkładu #2E7CB8, rozmiar czcionki z treścią pytania jest większy niż pozostałe treści, w jednym momencie można wybrać tylko jedno pole radio

    ok. 3 min
    Polecenie z arkusza

    Trzy pola radio z odpowiedziami: „Na Rysiance.”, „Na Wielkiej Raczy.”, „Na Wielkiej Rycerzowej.”. Jednocześnie można wybrać tylko jedno pole radio. Wyrównane do lewej strony pola typu radio.

    Trzy RadioButton wewnątrz jednej RadioGroup (R.3.2). Grupa pilnuje, żeby zaznaczona była najwyżej jedna odpowiedź: kliknięcie drugiej odznacza pierwszą (R.3.3). Przyciski radio poza grupą działałyby niezależnie - dałoby się zaznaczyć wszystkie trzy.

    Grupa nie ma layout_gravity, więc stoi przy lewej krawędzi. Każda odpowiedź ma identyfikator - kod podmieni ich treść przy kolejnych pytaniach. Zaznacz odpowiedzi w podglądzie.

    Typowe błędy

    Przyciski RadioButton wstawione wprost do LinearLayout, bez RadioGroup - można zaznaczyć kilka odpowiedzi naraz (R.3.3).

    Quiz/app/src/main/res/layout/activity_main.xml XML zmienione: 28 linii
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#2E7CB8"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <ImageView
            android:id="@+id/questionImage"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:layout_marginTop="40dp"
            android:contentDescription="Zdjęcie do pytania"
            android:src="@drawable/zad1" />
    
        <TextView
            android:id="@+id/questionText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="8dp"
            android:text="Które to schronisko?"
            android:textColor="#FFFFFF"
            android:textSize="24sp" />
    
        <RadioGroup
            android:id="@+id/answersGroup"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
    
            <RadioButton
                android:id="@+id/answerA"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Rysiance."
                android:textColor="#FFFFFF" />
    
            <RadioButton
                android:id="@+id/answerB"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Wielkiej Raczy."
                android:textColor="#FFFFFF" />
    
            <RadioButton
                android:id="@+id/answerC"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Wielkiej Rycerzowej."
                android:textColor="#FFFFFF" />
    
        </RadioGroup>
    
    </LinearLayout>
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  16. Krok 16: Przycisk DALEJ na środku

    Kryteria:

    R.3.2 Zdefiniowane kontrolki: obraz z pliku zad1.jpg , pole tekstowe z treścią pytania: „Które to schronisko?” trzy pola radio z odpowiedziami, oraz przycisk DALEJ

    ok. 1 min
    Polecenie z arkusza

    Przycisk DALEJ. Wyśrodkowane w poziomie obraz, treść pytania i przycisk.

    Przycisk o szerokości treści, wyśrodkowany layout_gravity="center_horizontal" (R.3.2). Biały napis przycisk ma domyślnie. Widok jest gotowy - zgodny z obrazem 1.

    Quiz/app/src/main/res/layout/activity_main.xml XML zmienione: 6 linii
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#2E7CB8"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <ImageView
            android:id="@+id/questionImage"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:layout_marginTop="40dp"
            android:contentDescription="Zdjęcie do pytania"
            android:src="@drawable/zad1" />
    
        <TextView
            android:id="@+id/questionText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="8dp"
            android:text="Które to schronisko?"
            android:textColor="#FFFFFF"
            android:textSize="24sp" />
    
        <RadioGroup
            android:id="@+id/answersGroup"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
    
            <RadioButton
                android:id="@+id/answerA"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Rysiance."
                android:textColor="#FFFFFF" />
    
            <RadioButton
                android:id="@+id/answerB"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Wielkiej Raczy."
                android:textColor="#FFFFFF" />
    
            <RadioButton
                android:id="@+id/answerC"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Wielkiej Rycerzowej."
                android:textColor="#FFFFFF" />
    
        </RadioGroup>
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="DALEJ" />
    
    </LinearLayout>
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  17. Etap 5: Git

    Krok 17 · po tym etapie masz 22 z 32 kryteriów (pkt)

    Krok 17: Repozytorium Git i pierwsza migawka: widok

    Kryteria: ,

    R.4.4 Skonfigurowane repozytorium wersji Git (email [email protected] , nazwa użytkownika - numer zdającego) co jest udokumentowane zrzutem ekranu

    R.4.5 Wykonane co najmniej dwa zapisy wersji do repozytorium wersji Git co jest udokumentowane zrzutem ekranu

    ok. 4 min
    Polecenie z arkusza

    Po przygotowaniu projektu utwórz lokalne repozytorium wersji Git, podczas pracy utwórz co najmniej 2 zapisy wersji: po utworzeniu widoku aplikacji oraz po zapisaniu działania aplikacji. Komentarze migawek (commit) powinny być znaczące. Skonfiguruj repozytorium Git: jako nazwę użytkownika użyj numer zdającego, jako email użytkownika [email protected]. UWAGA: Po utworzeniu widoku aplikacji zapisz stan kodu za pomocą Git.

    W Android Studio otwieramy kartę Terminal (katalog projektu) i wpisujemy kolejno:

    1. git init - tworzy puste repozytorium w folderze projektu,
    2. git config user.name i git config user.email - bez --global ustawienia dotyczą tylko tego repozytorium; na egzaminie nazwą jest numer PESEL - tu stoi Bartosz Bryniarski (R.4.4),
    3. git add . - dodaje wszystkie pliki projektu do migawki (szablon Android Studio ma gotowy .gitignore, więc pliki budowania nie trafią do repozytorium),
    4. git commit -m "..." - zapisuje migawkę ze znaczącym opisem (R.4.5).

    Podgląd to prawdziwy wynik tych poleceń.

    Typowe błędy

    git commit przed git config - Git odmawia zapisu („Author identity unknown”) albo podpisuje migawkę danymi systemowymi zamiast numeru zdającego (R.4.4).

    Wynik programu po tym kroku
    > git init
    Initialized empty Git repository in C:/Users/Egzamin/AndroidStudioProjects/Quiz/.git/
    > git config user.name "Bartosz Bryniarski"
    > git config user.email "[email protected]"
    > git add .
    > git commit -m "Widok quizu: obraz, pytanie, trzy odpowiedzi i przycisk DALEJ"
    [master (root-commit) 3f23f90] Widok quizu: obraz, pytanie, trzy odpowiedzi i przycisk DALEJ
     39 files changed, 895 insertions(+)
     create mode 100644 .gitignore
     create mode 100644 app/.gitignore
     create mode 100644 app/build.gradle.kts
     create mode 100644 app/proguard-rules.pro
     create mode 100644 app/src/main/AndroidManifest.xml
     create mode 100644 app/src/main/java/com/example/quiz/MainActivity.java
     create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml
     create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml
     create mode 100644 app/src/main/res/drawable/zad1.jpg
     create mode 100644 app/src/main/res/drawable/zad2.jpg
     create mode 100644 app/src/main/res/drawable/zad3.jpg
     create mode 100644 app/src/main/res/layout/activity_main.xml
     create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
     create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
     create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp
     create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
     create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp
     create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
     create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp
     create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
     create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
     create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
     create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
     create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
     create mode 100644 app/src/main/res/values-night/themes.xml
     create mode 100644 app/src/main/res/values/colors.xml
     create mode 100644 app/src/main/res/values/strings.xml
     create mode 100644 app/src/main/res/values/themes.xml
     create mode 100644 app/src/main/res/xml/backup_rules.xml
     create mode 100644 app/src/main/res/xml/data_extraction_rules.xml
     create mode 100644 build.gradle.kts
     create mode 100644 gradle.properties
     create mode 100644 gradle/gradle-daemon-jvm.properties
     create mode 100644 gradle/libs.versions.toml
     create mode 100644 gradle/wrapper/gradle-wrapper.jar
     create mode 100644 gradle/wrapper/gradle-wrapper.properties
     create mode 100644 gradlew
     create mode 100644 gradlew.bat
     create mode 100644 settings.gradle.kts
  18. Etap 6: Aplikacja mobilna - działanie

    Kroki 18-21 · po tym etapie masz 28 z 32 kryteriów (pkt)

    Krok 18: Pytania w tablicach

    Kryteria:

    R.3.4 Umieszczone treść pytań i odpowiedzi w tablicy, liście lub innej kolekcji. Aplikacja zawsze działa na wszystkich elementach kolekcji np. za pomocą pętli

    ok. 4 min
    Polecenie z arkusza

    Treść pytań i odpowiedzi można skopiować z pliku pytania.txt wypakowanego z archiwum ZIP. Wszystkie pytania należy zapisać w tablicy, liście lub innej kolekcji.

    Pytania zapisujemy w tablicach - element o indeksie 0 każdej z nich dotyczy pierwszego pytania, 1 - drugiego, 2 - trzeciego (R.3.4):

    • images - identyfikatory zdjęć z drawable,
    • questions - treści pytań,
    • answers - tablica dwuwymiarowa: wiersz to pytanie, kolumny to odpowiedzi A, B, C,
    • correctAnswers - indeks poprawnej odpowiedzi: „Na Wielkiej Raczy.” to 1, „owczarek.” - 0, „Tatry.” - 2.

    currentQuestion mówi, które pytanie jest na ekranie, points liczy punkty.

    Quiz/app/src/main/java/com/example/quiz/MainActivity.java Java zmienione: 11 linii
    package com.example.quiz;
    
    import android.os.Bundle;
    
    import androidx.activity.EdgeToEdge;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.graphics.Insets;
    import androidx.core.view.ViewCompat;
    import androidx.core.view.WindowInsetsCompat;
    
    public class MainActivity extends AppCompatActivity {
    
        private final int[] images = {R.drawable.zad1, R.drawable.zad2, R.drawable.zad3};
        private final String[] questions = {"Które to schronisko?", "Zwierzę na zdjęciu to", "W oddali są widoczne"};
        private final String[][] answers = {
                {"Na Rysiance.", "Na Wielkiej Raczy.", "Na Wielkiej Rycerzowej."},
                {"owczarek.", "wilk.", "kozica."},
                {"Himalaje.", "Alpy.", "Tatry."}
        };
        private final int[] correctAnswers = {1, 0, 2};
        private int currentQuestion = 0;
        private int points = 0;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            EdgeToEdge.enable(this);
            setContentView(R.layout.activity_main);
            ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
                Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
                v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
                return insets;
            });
        }
    }

    Wynik programu bez zmian - taki jak po kroku 17.

  19. Krok 19: showQuestion: pytanie z tablic na ekranie

    Kryteria: , ,

    R.3.4 Umieszczone treść pytań i odpowiedzi w tablicy, liście lub innej kolekcji. Aplikacja zawsze działa na wszystkich elementach kolekcji np. za pomocą pętli

    R.3.6 Program odwołuje się do przynajmniej jednej kontrolki w sposób zgodny z danym środowiskiem programistycznym

    R.1.3 Polskie lub angielskie, znaczące nazewnictwo metod

    ok. 4 min
    Polecenie z arkusza

    Aplikacja jest uniwersalna - zawsze działa na wszystkich elementach kolekcji. Wszystkie pola radio nie są zaznaczone.

    Kontrolki znajdujemy przez findViewById (R.3.6). Trzy przyciski radio wkładamy do tablicy answerButtons - dzięki temu ustawiamy ich treść pętlą po indeksach, a nie trzema osobnymi instrukcjami (R.3.4).

    Metoda showQuestion wyświetla pytanie o indeksie currentQuestion: zdjęcie (setImageResource), treść i trzy odpowiedzi, a na końcu clearCheck() odznacza wszystkie przyciski grupy. Wywołujemy ją w onCreate - ekran startowy bierze dane z tablic, nie z XML.

    Quiz/app/src/main/java/com/example/quiz/MainActivity.java Java zmienione: 24 linie
    package com.example.quiz;
    
    import android.os.Bundle;
    import android.widget.ImageView;
    import android.widget.RadioButton;
    import android.widget.RadioGroup;
    import android.widget.TextView;
    
    import androidx.activity.EdgeToEdge;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.graphics.Insets;
    import androidx.core.view.ViewCompat;
    import androidx.core.view.WindowInsetsCompat;
    
    public class MainActivity extends AppCompatActivity {
    
        private final int[] images = {R.drawable.zad1, R.drawable.zad2, R.drawable.zad3};
        private final String[] questions = {"Które to schronisko?", "Zwierzę na zdjęciu to", "W oddali są widoczne"};
        private final String[][] answers = {
                {"Na Rysiance.", "Na Wielkiej Raczy.", "Na Wielkiej Rycerzowej."},
                {"owczarek.", "wilk.", "kozica."},
                {"Himalaje.", "Alpy.", "Tatry."}
        };
        private final int[] correctAnswers = {1, 0, 2};
        private int currentQuestion = 0;
        private int points = 0;
    
        private ImageView questionImage;
        private TextView questionText;
        private RadioGroup answersGroup;
        private RadioButton[] answerButtons;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            EdgeToEdge.enable(this);
            setContentView(R.layout.activity_main);
            ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
                Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
                v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
                return insets;
            });
    
            questionImage = findViewById(R.id.questionImage);
            questionText = findViewById(R.id.questionText);
            answersGroup = findViewById(R.id.answersGroup);
            answerButtons = new RadioButton[]{findViewById(R.id.answerA), findViewById(R.id.answerB), findViewById(R.id.answerC)};
            showQuestion();
        }
    
        private void showQuestion() {
            questionImage.setImageResource(images[currentQuestion]);
            questionText.setText(questions[currentQuestion]);
            for (int i = 0; i < answerButtons.length; i++) {
                answerButtons[i].setText(answers[currentQuestion][i]);
            }
            answersGroup.clearCheck();
        }
    }

    Wynik programu bez zmian - taki jak po kroku 17.

  20. Krok 20: DALEJ: punkt za prawidłową odpowiedź

    Kryteria: ,

    R.3.5 Program obsługuje zdarzenie kliknięcia przycisku w sposób właściwy dla danego środowiska programistycznego. Wystarczy, że kontrolka przycisku jest odpowiednio powiązana ze zdarzeniem

    R.3.10 Jeżeli wybrana odpowiedź jest prawidłowa, inkrementowana jest liczba punktów

    ok. 3 min
    Polecenie z arkusza

    Po wybraniu przycisku DALEJ: inkrementowana jest liczba punktów, jeśli wybrano prawidłową odpowiedź.

    Metodę nextQuestion wiążemy z przyciskiem atrybutem android:onClick (R.3.5). correctAnswers[currentQuestion] to indeks poprawnej odpowiedzi bieżącego pytania, a answerButtons[...] - przycisk radio z tą odpowiedzią. Jeśli jest zaznaczony, points++ (R.3.10).

    Na razie przycisk tylko liczy punkty - ekran się nie zmienia.

    Quiz/app/src/main/java/com/example/quiz/MainActivity.java Java zmienione: 7 linii
    package com.example.quiz;
    
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.RadioButton;
    import android.widget.RadioGroup;
    import android.widget.TextView;
    
    import androidx.activity.EdgeToEdge;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.graphics.Insets;
    import androidx.core.view.ViewCompat;
    import androidx.core.view.WindowInsetsCompat;
    
    public class MainActivity extends AppCompatActivity {
    
        private final int[] images = {R.drawable.zad1, R.drawable.zad2, R.drawable.zad3};
        private final String[] questions = {"Które to schronisko?", "Zwierzę na zdjęciu to", "W oddali są widoczne"};
        private final String[][] answers = {
                {"Na Rysiance.", "Na Wielkiej Raczy.", "Na Wielkiej Rycerzowej."},
                {"owczarek.", "wilk.", "kozica."},
                {"Himalaje.", "Alpy.", "Tatry."}
        };
        private final int[] correctAnswers = {1, 0, 2};
        private int currentQuestion = 0;
        private int points = 0;
    
        private ImageView questionImage;
        private TextView questionText;
        private RadioGroup answersGroup;
        private RadioButton[] answerButtons;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            EdgeToEdge.enable(this);
            setContentView(R.layout.activity_main);
            ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
                Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
                v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
                return insets;
            });
    
            questionImage = findViewById(R.id.questionImage);
            questionText = findViewById(R.id.questionText);
            answersGroup = findViewById(R.id.answersGroup);
            answerButtons = new RadioButton[]{findViewById(R.id.answerA), findViewById(R.id.answerB), findViewById(R.id.answerC)};
            showQuestion();
        }
    
        private void showQuestion() {
            questionImage.setImageResource(images[currentQuestion]);
            questionText.setText(questions[currentQuestion]);
            for (int i = 0; i < answerButtons.length; i++) {
                answerButtons[i].setText(answers[currentQuestion][i]);
            }
            answersGroup.clearCheck();
        }
    
        public void nextQuestion(View view) {
            if (answerButtons[correctAnswers[currentQuestion]].isChecked()) {
                points++;
            }
        }
    }
    Quiz/app/src/main/res/layout/activity_main.xml XML zmienione: 1 linia
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#2E7CB8"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <ImageView
            android:id="@+id/questionImage"
            android:layout_width="match_parent"
            android:layout_height="250dp"
            android:layout_marginTop="40dp"
            android:contentDescription="Zdjęcie do pytania"
            android:src="@drawable/zad1" />
    
        <TextView
            android:id="@+id/questionText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="8dp"
            android:text="Które to schronisko?"
            android:textColor="#FFFFFF"
            android:textSize="24sp" />
    
        <RadioGroup
            android:id="@+id/answersGroup"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
    
            <RadioButton
                android:id="@+id/answerA"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Rysiance."
                android:textColor="#FFFFFF" />
    
            <RadioButton
                android:id="@+id/answerB"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Wielkiej Raczy."
                android:textColor="#FFFFFF" />
    
            <RadioButton
                android:id="@+id/answerC"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Na Wielkiej Rycerzowej."
                android:textColor="#FFFFFF" />
    
        </RadioGroup>
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:onClick="nextQuestion"
            android:text="DALEJ" />
    
    </LinearLayout>

    Wynik programu bez zmian - taki jak po kroku 17.

  21. Krok 21: DALEJ: następne pytanie, po ostatnim - pierwsze

    Kryteria: , ,

    R.3.8 Po wybraniu przycisku jeżeli istnieje następne pytanie w quizie, jest ono wyświetlane oraz wszystkie przyciski radio nie są zaznaczone (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    R.3.9 Po wybraniu przycisku jeżeli jest to ostatnie pytanie, jest wyświetlane pierwsze pytanie (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    R.3.4 Umieszczone treść pytań i odpowiedzi w tablicy, liście lub innej kolekcji. Aplikacja zawsze działa na wszystkich elementach kolekcji np. za pomocą pętli

    ok. 3 min
    Polecenie z arkusza

    Jeżeli istnieją następne pytania w quizie, wyświetlane jest kolejne pytanie. W przeciwnym wypadku wyświetlane jest ponownie pytanie pierwsze. Wszystkie pola radio nie są zaznaczone.

    Indeks zwiększamy o 1 i bierzemy resztę z dzielenia przez liczbę pytań: 0 → 1 → 2 → 0 (R.3.8, R.3.9). Liczba pytań to questions.length, a nie wpisane na sztywno 3 - po dopisaniu czwartego pytania do tablic quiz obejmie je bez zmian w kodzie (R.3.4). showQuestion() wyświetla nowe pytanie i odznacza przyciski radio (R.3.8).

    Zaznacz odpowiedź i kliknij „DALEJ” trzy razy - po trzecim pytaniu wraca pierwsze, jak na obrazie 1.

    Typowe błędy

    currentQuestion++ bez powrotu do zera - po ostatnim pytaniu questions[3] zatrzymuje aplikację z ArrayIndexOutOfBoundsException (R.3.9).

    Quiz/app/src/main/java/com/example/quiz/MainActivity.java Java zmienione: 2 linie
    package com.example.quiz;
    
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.RadioButton;
    import android.widget.RadioGroup;
    import android.widget.TextView;
    
    import androidx.activity.EdgeToEdge;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.graphics.Insets;
    import androidx.core.view.ViewCompat;
    import androidx.core.view.WindowInsetsCompat;
    
    public class MainActivity extends AppCompatActivity {
    
        private final int[] images = {R.drawable.zad1, R.drawable.zad2, R.drawable.zad3};
        private final String[] questions = {"Które to schronisko?", "Zwierzę na zdjęciu to", "W oddali są widoczne"};
        private final String[][] answers = {
                {"Na Rysiance.", "Na Wielkiej Raczy.", "Na Wielkiej Rycerzowej."},
                {"owczarek.", "wilk.", "kozica."},
                {"Himalaje.", "Alpy.", "Tatry."}
        };
        private final int[] correctAnswers = {1, 0, 2};
        private int currentQuestion = 0;
        private int points = 0;
    
        private ImageView questionImage;
        private TextView questionText;
        private RadioGroup answersGroup;
        private RadioButton[] answerButtons;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            EdgeToEdge.enable(this);
            setContentView(R.layout.activity_main);
            ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
                Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
                v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
                return insets;
            });
    
            questionImage = findViewById(R.id.questionImage);
            questionText = findViewById(R.id.questionText);
            answersGroup = findViewById(R.id.answersGroup);
            answerButtons = new RadioButton[]{findViewById(R.id.answerA), findViewById(R.id.answerB), findViewById(R.id.answerC)};
            showQuestion();
        }
    
        private void showQuestion() {
            questionImage.setImageResource(images[currentQuestion]);
            questionText.setText(questions[currentQuestion]);
            for (int i = 0; i < answerButtons.length; i++) {
                answerButtons[i].setText(answers[currentQuestion][i]);
            }
            answersGroup.clearCheck();
        }
    
        public void nextQuestion(View view) {
            if (answerButtons[correctAnswers[currentQuestion]].isChecked()) {
                points++;
            }
            currentQuestion = (currentQuestion + 1) % questions.length;
            showQuestion();
        }
    }

    Wynik programu bez zmian - taki jak po kroku 17.

  22. Etap 7: Git

    Krok 22 · po tym etapie masz 28 z 32 kryteriów (pkt)

    Krok 22: Druga migawka i log z konfiguracją

    Kryteria: ,

    R.4.4 Skonfigurowane repozytorium wersji Git (email [email protected] , nazwa użytkownika - numer zdającego) co jest udokumentowane zrzutem ekranu

    R.4.5 Wykonane co najmniej dwa zapisy wersji do repozytorium wersji Git co jest udokumentowane zrzutem ekranu

    ok. 3 min
    Polecenie z arkusza

    UWAGA: Po zaprogramowaniu aplikacji zapisz stan kodu za pomocą Git. Wykorzystując polecenia Git lub wbudowane narzędzia systemu kontroli wersji w środowisku programistycznym wypisz wszystkie zapisane stany kodu (migawki) wraz z komentarzami oraz z konfiguracją użytkownika Git (email i nazwa). Wykonaj zrzut/zrzuty ekranu dokumentujące tą czynność. Zrzuty nazwij git1.png, git2.png, ... i zapisz w folderze dokumentacja.

    Druga migawka po zaprogramowaniu działania: git add . i git commit ze znaczącym opisem (R.4.5). Potem:

    • git log - wszystkie migawki z autorem, datą i komentarzem,
    • git config user.name i git config user.email - bez wartości wypisują bieżącą konfigurację.

    Zrzut terminala z tymi wynikami zapisujemy jako git1.png w folderze dokumentacja (R.4.4, R.4.5). Podgląd to prawdziwy wynik poleceń.

    Wynik programu po tym kroku
    > git add .
    > git commit -m "Działanie quizu: pytania w tablicach, punkty i przejście do kolejnego pytania"
    [master ff44842] Działanie quizu: pytania w tablicach, punkty i przejście do kolejnego pytania
     2 files changed, 45 insertions(+)
    > git log
    commit ff448423faef73bbd1e796582e94e482009b05ed
    Author: Bartosz Bryniarski <[email protected]>
    Date:   Fri Jan 9 10:15:00 2026 +0100
    
        Działanie quizu: pytania w tablicach, punkty i przejście do kolejnego pytania
    
    commit 3f23f90119833a77e380eb8b16270e9d661b526e
    Author: Bartosz Bryniarski <[email protected]>
    Date:   Fri Jan 9 10:15:00 2026 +0100
    
        Widok quizu: obraz, pytanie, trzy odpowiedzi i przycisk DALEJ
    > git config user.name
    Bartosz Bryniarski
    > git config user.email
    [email protected]
  23. Etap 8: Dokumentacja i oddanie pracy

    Kroki 23-25 · po tym etapie masz 32 z 32 kryteriów (pkt)

    Krok 23: Uruchomienie w emulatorze i zrzuty mobile

    Kryteria: ,

    R.3.7 Aplikacja kompiluje się i uruchamia w emulatorze, co jest udokumentowane zrzutem ekranu jej układ jest zgodny z obrazem 1 lub obrazem 2 w arkuszu egzaminacyjnym (w uruchomionej aplikacji lub na zrzucie i obowiązkowo w kodzie)

    R.4.3 Zapisany przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji mobilnej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja

    ok. 3 min
    Polecenie z arkusza

    Podejmij próbę kompilacji i emulacji aplikacji. Wykonaj zrzuty ekranu dokumentujące działanie aplikacji mobilnej: mobile1, mobile2 ... (np. stan początkowy, po wybraniu odpowiedzi i wciśnięciu przycisku, z kolejnym pytaniem). Zrzuty powinny zawierać cały obszar ekranu z widocznym paskiem zadań.

    Uruchamiamy aplikację w emulatorze. Zrzuty całego ekranu z Android Studio do folderu dokumentacja: mobile1 - stan początkowy, mobile2 - zaznaczona odpowiedź, mobile3 - kolejne pytanie (R.3.7, R.4.3).

    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  24. Krok 24: Plik egzamin

    Kryteria:

    R.4.6 Dokumentacja zawiera: nazwę systemu operacyjnego, nazwy środowisk programistycznych, emulatora, nazwy języków programowania

    ok. 2 min
    Polecenie z arkusza

    Utwórz plik tekstowy z dokumentacją i nazwij go egzamin. Dokument powinien zawierać informacje o narzędziach wykorzystanych na egzaminie: nazwę systemu operacyjnego, nazwy środowisk programistycznych, nazwę emulatora dla aplikacji mobilnej, nazwy języków programowania.

    Tym razem arkusz wymaga pliku tekstowego - wystarczy Notatnik: egzamin.txt w folderze dokumentacja z systemem, środowiskami, emulatorem i językami (R.4.6).

    egzamin.txt Tekst nowy plik
    System operacyjny: Windows 11
    Środowiska programistyczne: IntelliJ IDEA (aplikacja konsolowa), Android Studio (aplikacja mobilna), Git
    Emulator: Pixel 8 (Android 16, API 36) z Android Studio
    Języki programowania: Java (aplikacja konsolowa i mobilna), XML (układ aplikacji mobilnej)

    Ekran telefonu bez zmian - taki jak po kroku 23.

  25. Krok 25: Folder mobilna i nagranie płyty

    Kryteria:

    R.4.7 Przygotowana jest dokumentacja w postaci płyty: oba projekty są spakowane oraz zmodyfikowane pliki są na zewnątrz archiwów

    ok. 5 min
    Polecenie z arkusza

    Kod aplikacji przygotuj do nagrania na płytę. W folderze mobilna powinno znaleźć się archiwum całego projektu o nazwie mobilna.zip, skopiowane pliki z kodem źródłowym, które były modyfikowane. Nagraj płytę z rezultatami pracy. W folderze z numerem zdającego powinny się znajdować foldery: dokumentacja, konsolowa, mobilna.

    Folder projektu Android - razem z ukrytym folderem .git, w którym są migawki - pakujemy do mobilna.zip; obok kopiujemy activity_main.xml i MainActivity.java. Sprawdzamy zawartość przed nagraniem (R.4.7):

    • konsolowa: konsola.zip, Pytanie.java, PytanieZamkniete.java, Main.java,
    • mobilna: mobilna.zip, activity_main.xml, MainActivity.java,
    • dokumentacja: test1.png, test2.png, mobile1 - mobile3, git1.png, egzamin.txt.

    Nagrywamy, sprawdzamy odczyt płyty, opisujemy ją numerem zdającego.

    Ekran telefonu bez zmian - taki jak po kroku 23.

  26. Etap 9: Dobre praktyki (poza kluczem)

    Kroki 26-27 · po tym etapie masz 32 z 32 kryteriów (pkt)

    Krok 26: Konsola: odpowiedź „b” to też „B”

    ok. 2 min
    Polecenie z arkusza

    Metoda ma jeden argument o typie znakowym, który przyjmuje wartości ‘A’ lub ‘B’ lub ‘C’ (odpowiedzi użytkownika na pytanie quizu).

    Porównanie znaków rozróżnia wielkość liter: odpowiedź b przy poprawnej B dała wynik „Odpowiedź nieprawidłowa”. Character.toUpperCase zamienia wpisany znak na wielką literę, a trim() usuwa spację wpisaną przed literą.

    Podgląd: poprawna odpowiedź wpisana małą literą. Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    konsola/src/Main.java Java zmienione: 2 linie
    import java.util.Scanner;
    
    public class Main {
        public static void main(String[] args) {
            // Test 1: obiektu klasy abstrakcyjnej nie można utworzyć - błąd kompilacji (zrzut test1.png)
            // Pytanie pytanie = new Pytanie("Które to schronisko?", "zad1.jpg");
    
            Scanner scanner = new Scanner(System.in);
            System.out.print("Treść pytania: ");
            String tresc = scanner.nextLine();
            System.out.print("Nazwa pliku obrazu: ");
            String plikObrazu = scanner.nextLine();
            System.out.print("Odpowiedź A: ");
            String odpowiedzA = scanner.nextLine();
            System.out.print("Odpowiedź B: ");
            String odpowiedzB = scanner.nextLine();
            System.out.print("Odpowiedź C: ");
            String odpowiedzC = scanner.nextLine();
            System.out.print("Poprawna odpowiedź (A, B lub C): ");
            char poprawna = Character.toUpperCase(scanner.nextLine().trim().charAt(0));
    
            PytanieZamkniete pytanie = new PytanieZamkniete(tresc, plikObrazu, odpowiedzA, odpowiedzB, odpowiedzC, poprawna);
    
            System.out.print("Twoja odpowiedź: ");
            char odpowiedz = Character.toUpperCase(scanner.nextLine().trim().charAt(0));
            if (pytanie.sprawdzOdpowiedz(odpowiedz)) {
                System.out.println("Odpowiedź prawidłowa");
            } else {
                System.out.println("Odpowiedź nieprawidłowa");
            }
        }
    }
    Wynik programu po tym kroku
    > java Main
    Treść pytania: Które to schronisko?
    Nazwa pliku obrazu: zad1.jpg
    Odpowiedź A: Na Rysiance.
    Odpowiedź B: Na Wielkiej Raczy.
    Odpowiedź C: Na Wielkiej Rycerzowej.
    Poprawna odpowiedź (A, B lub C): b
    Twoja odpowiedź: b
    Odpowiedź prawidłowa
  27. Krok 27: Android: wynik po ostatnim pytaniu

    ok. 2 min
    Polecenie z arkusza

    Inkrementowana jest liczba punktów, jeśli wybrano prawidłową odpowiedź.

    Aplikacja liczy punkty, ale nigdzie ich nie pokazuje. Po ostatnim pytaniu, gdy indeks wraca do zera, pokazujemy krótki komunikat Toast z wynikiem, np. „Wynik: 2 / 3”, i zerujemy punkty - nowa runda zaczyna się od zera.

    Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    Quiz/app/src/main/java/com/example/quiz/MainActivity.java Java zmienione: 5 linii
    package com.example.quiz;
    
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.RadioButton;
    import android.widget.RadioGroup;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import androidx.activity.EdgeToEdge;
    import androidx.appcompat.app.AppCompatActivity;
    import androidx.core.graphics.Insets;
    import androidx.core.view.ViewCompat;
    import androidx.core.view.WindowInsetsCompat;
    
    public class MainActivity extends AppCompatActivity {
    
        private final int[] images = {R.drawable.zad1, R.drawable.zad2, R.drawable.zad3};
        private final String[] questions = {"Które to schronisko?", "Zwierzę na zdjęciu to", "W oddali są widoczne"};
        private final String[][] answers = {
                {"Na Rysiance.", "Na Wielkiej Raczy.", "Na Wielkiej Rycerzowej."},
                {"owczarek.", "wilk.", "kozica."},
                {"Himalaje.", "Alpy.", "Tatry."}
        };
        private final int[] correctAnswers = {1, 0, 2};
        private int currentQuestion = 0;
        private int points = 0;
    
        private ImageView questionImage;
        private TextView questionText;
        private RadioGroup answersGroup;
        private RadioButton[] answerButtons;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            EdgeToEdge.enable(this);
            setContentView(R.layout.activity_main);
            ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
                Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
                v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
                return insets;
            });
    
            questionImage = findViewById(R.id.questionImage);
            questionText = findViewById(R.id.questionText);
            answersGroup = findViewById(R.id.answersGroup);
            answerButtons = new RadioButton[]{findViewById(R.id.answerA), findViewById(R.id.answerB), findViewById(R.id.answerC)};
            showQuestion();
        }
    
        private void showQuestion() {
            questionImage.setImageResource(images[currentQuestion]);
            questionText.setText(questions[currentQuestion]);
            for (int i = 0; i < answerButtons.length; i++) {
                answerButtons[i].setText(answers[currentQuestion][i]);
            }
            answersGroup.clearCheck();
        }
    
        public void nextQuestion(View view) {
            if (answerButtons[correctAnswers[currentQuestion]].isChecked()) {
                points++;
            }
            currentQuestion = (currentQuestion + 1) % questions.length;
            if (currentQuestion == 0) {
                Toast.makeText(this, "Wynik: " + points + " / " + questions.length, Toast.LENGTH_LONG).show();
                points = 0;
            }
            showQuestion();
        }
    }
    Ekran telefonu po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony). Treść arkusza i klucza oceniania pochodzi z CKE.