Demo 1 (łatwe): Formularz powitania

Writing /home/grochu/public_html/wiki/data/meta/zajecia/java/sidebar.meta failed
Unable to save metadata file. Hint: disk full; file permissions; safe_mode setting.

Cel: szybkie przejście przez JFrame, JTextField, JButton, JLabel, listener.

Zadanie demonstracyjne:

  • Dwa pola: imię i nazwisko
  • Przycisk „Przywitaj”
  • Etykieta z wynikiem „Witaj, Imię Nazwisko”

Wskazówki:

  • Użyj JPanel + GridLayout(0, 2, 8, 8)
  • Logikę kliknięcia dodaj przez addActionListener
  • Odczytuj tekst przez getText().trim()

Rozwiązanie:

public class DemoGreeting {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(DemoGreeting::showUi);
    }
 
    static void showUi() {
        JFrame f = new JFrame("Powitanie");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
        JTextField tfFirst = new JTextField();
        JTextField tfLast = new JTextField();
        JButton btn = new JButton("Przywitaj");
        JLabel out = new JLabel(" ");
 
        JPanel p = new JPanel(new GridLayout(0, 2, 8, 8));
        p.add(new JLabel("Imię:"));
        p.add(tfFirst);
        p.add(new JLabel("Nazwisko:"));
        p.add(tfLast);
        p.add(btn);
        p.add(out);
 
        btn.addActionListener(e -> {
            String full = tfFirst.getText().trim() + " " + tfLast.getText().trim();
            out.setText("Witaj, " + full.trim());
        });
 
        f.setContentPane(p);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

Demo 2 (średnie): Lista z modelem danych

Cel: DefaultListModel, JList, operacje dodaj/usuń.

Zadanie demonstracyjne:

  • Pole tekstowe + przycisk Dodaj
  • Lista elementów
  • Przycisk Usuń zaznaczony

Wskazówki:

  • Dane trzymaj w DefaultListModel<String>
  • list.getSelectedIndex() zwraca pozycję zaznaczenia
  • Przy braku zaznaczenia pokaż JOptionPane

Rozwiązanie:

public class DemoListModel {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame("Lista z modelem");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
            DefaultListModel<String> model = new DefaultListModel<>();
            JList<String> list = new JList<>(model);
            JTextField tf = new JTextField(16);
            JButton add = new JButton("Dodaj");
            JButton remove = new JButton("Usuń zaznaczony");
 
            add.addActionListener(e -> {
                String text = tf.getText().trim();
                if (!text.isEmpty()) {
                    model.addElement(text);
                    tf.setText("");
                }
            });
 
            remove.addActionListener(e -> {
                int idx = list.getSelectedIndex();
                if (idx >= 0) {
                    model.remove(idx);
                } else {
                    JOptionPane.showMessageDialog(f, "Najpierw zaznacz element");
                }
            });
 
            JPanel top = new JPanel();
            top.add(tf);
            top.add(add);
            top.add(remove);
 
            f.add(top, BorderLayout.NORTH);
            f.add(new JScrollPane(list), BorderLayout.CENTER);
            f.setSize(520, 320);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });
    }
}

Demo 3 (trudniejsze): Tabela i Action

Cel: DefaultTableModel, wspólna akcja dla przycisku i menu.

Zadanie demonstracyjne:

  • Tabela: ID, Nazwa, Cena
  • Akcja „Dodaj przykładowy wiersz”
  • Ta sama akcja w menu i na toolbarze

Wskazówki:

  • Utwórz Action dziedzicząc po AbstractAction
  • W actionPerformed modyfikuj DefaultTableModel
  • Jeden obiekt Action podłącz do JButton i JMenuItem

Rozwiązanie:

public class DemoTableAction {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame("Tabela + Action");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
            DefaultTableModel model = new DefaultTableModel(new Object[]{"ID", "Nazwa", "Cena"}, 0);
            JTable table = new JTable(model);
 
            Action addSample = new AbstractAction("Dodaj próbkę") {
                int id = 1;
                @Override
                public void actionPerformed(java.awt.event.ActionEvent e) {
                    model.addRow(new Object[]{id++, "Produkt", 19.99});
                }
            };
 
            JToolBar bar = new JToolBar();
            bar.add(new JButton(addSample));
 
            JMenu file = new JMenu("Dane");
            file.add(new JMenuItem(addSample));
            JMenuBar mb = new JMenuBar();
            mb.add(file);
            f.setJMenuBar(mb);
 
            f.add(bar, BorderLayout.NORTH);
            f.add(new JScrollPane(table), BorderLayout.CENTER);
            f.setSize(640, 360);
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });
    }
}

Zadania do samodzielnego wykonania - wybór

  • Zadanie A (łatwe, 15-20 min): Kalkulator BMI
  • Zadanie B (średnie, 20-25 min): Notatnik TODO z JList
  • Zadanie C (średnie+, 25-30 min): Formularz rejestracji z walidacją
  • Zadanie D (trudniejsze, 30+ min): Mini CRM w JTable + DefaultTableModel

Zadanie A: Kalkulator BMI

Wymagania:

  • Pola: wzrost (cm), masa (kg)
  • Przycisk „Oblicz”
  • Wynik BMI i kategoria (niedowaga, norma, nadwaga, otyłość)

Wskazówki:

  • Parsuj liczby przez Double.parseDouble
  • Obsłuż NumberFormatException i pokaż komunikat JOptionPane
  • Kategorie BMI trzymaj w oddzielnej metodzie

Rozwiązanie:

static String bmiCategory(double bmi) {
    if (bmi < 18.5) return "Niedowaga";
    if (bmi < 25.0) return "Norma";
    if (bmi < 30.0) return "Nadwaga";
    return "Otyłość";
}
 
// fragment listenera
try {
    double cm = Double.parseDouble(tfHeight.getText().trim());
    double kg = Double.parseDouble(tfWeight.getText().trim());
    double m = cm / 100.0;
    double bmi = kg / (m * m);
    out.setText(String.format("BMI: %.2f (%s)", bmi, bmiCategory(bmi)));
} catch (NumberFormatException ex) {
    JOptionPane.showMessageDialog(frame, "Podaj poprawne liczby");
}

Zadanie B: Notatnik TODO (JList)

Wymagania:

  • Dodawanie pozycji do listy
  • Usuwanie zaznaczonej pozycji
  • Oznaczanie zadania jako wykonane (np. prefiks „[x]”)

Wskazówki:

  • Użyj DefaultListModel<String>
  • Aktualizuj element przez set(index, value)
  • Akcje „Dodaj” i „Usuń” mogą korzystać z osobnych Action

Rozwiązanie:

int i = list.getSelectedIndex();
if (i >= 0) {
    String old = model.get(i);
    if (!old.startsWith("[x] ")) {
        model.set(i, "[x] " + old);
    }
}

Zadanie C: Formularz rejestracji

Wymagania:

  • Pola: email, hasło, potwierdzenie hasła
  • Checkbox akceptacji regulaminu
  • Przycisk „Zarejestruj” aktywny tylko gdy walidacja przejdzie

Wskazówki:

  • Dla hasła użyj JPasswordField
  • Walidację uruchamiaj po każdej zmianie (np. DocumentListener)
  • Wydziel metodę validateForm() i w niej steruj setEnabled

Rozwiązanie:

void validateForm() {
    String email = tfEmail.getText().trim();
    String pass1 = new String(pf1.getPassword());
    String pass2 = new String(pf2.getPassword());
 
    boolean okEmail = email.contains("@") && email.contains(".");
    boolean okPass = pass1.length() >= 8 && pass1.equals(pass2);
    boolean okTerms = cbTerms.isSelected();
 
    btnRegister.setEnabled(okEmail && okPass && okTerms);
}

Zadanie D: Mini CRM (JTable)

Wymagania:

  • Tabela klientów: ID, Imię, Miasto, Status
  • Dodawanie i usuwanie rekordów
  • Filtrowanie po mieście

Wskazówki:

  • Dane przechowuj w DefaultTableModel
  • Filtrowanie zrealizuj przez TableRowSorter
  • Każda operacja UI ma aktualizować tylko model, nie widok bezpośrednio

Rozwiązanie:

DefaultTableModel model = new DefaultTableModel(new Object[]{"ID", "Imię", "Miasto", "Status"}, 0);
JTable table = new JTable(model);
TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<>(model);
table.setRowSorter(sorter);
 
// fragment filtrowania
String city = tfCityFilter.getText().trim();
if (city.isEmpty()) {
    sorter.setRowFilter(null);
} else {
    sorter.setRowFilter(RowFilter.regexFilter("(?i)" + java.util.regex.Pattern.quote(city), 2));
}

Zadanie 6 Kalkulator

Utwórz prosty, czterodziałaniowy kalkulator wykorzystujący Swing dla języka Java. Przy przygotowaniu GUI inspiruj się kalkulatorem systemu Windows. Zadbaj o obsługę liczb niecałkowitych.

Wskazówki:

  • Użyj JTextField do wyświetlania aktualnego wyniku i wprowadzania liczb
  • Przyciski dla cyfr (0-9) i działań (+, -, *, /)
  • Obsłuż zdarzenia kliknięć przycisków przez ActionListener
  • Przechowuj aktualną wartość i ostatnią operację, aby umożliwić ciągłe obliczenia