Java Swing

    -- Sebastian Pawlak, 2010.


Swing 1

Temat: Kontrola zawartości pola tekstowego

Celem programu było stworzenie pola tekstowego, do którego użytkownik będzie mógł wprowadzać wyłącznie cyfry (bez możliwości użycia znaku minusa), a próba wpisania innych znaków zakończy się sygnałem dźwiękowym. Ponadto kontrolowane jest to, czy wprowadzona liczba mieści się w pożądanym przedziale -- jeśli nie, to tło zmienia kolor na czerwony. Nie będzie można wprowadzić także większej liczby znaków niż to ustalono. Kontrola odbywa się na bieżąco (znak po znaku), a nie dopiero po naciśnięciu klawisza Enter.
    Jak już wspomniano, do informowania użytkownika o tym, że wprowadzona wartość jest spoza dopuszczalnego przedziału wybrano metodę polegającą na podświetlaniu tła na czerwono. Istnieje możliwość napisania programu w ten sposób, że w ogóle nie byłoby możliwości wprowadzenia takiej wartości, która byłaby spoza dopuszczalnego przedziału -- chodzi o to, że w żadnym momencie w polu tekstowym nie występowałaby wartość spoza przedziału. W tym przypadku wprowadzenie bądź usunięcie znaków, w efekcie czego nowa wartość w polu tekstowym byłaby spoza przedziału, byłoby ignorowane. Jednak w praktyce taki sposób działania jest bardzo niewygodny dla użytkownika, zwłaszcza gdy dolna wartość przedziału jest większa od zera. Z tego względu nie wybrano tej właśnie metody.
    W programie zastosowano podejście polegające na stworzeniu klasy dziedziczącej po javax.swing.text.PlainDocument i własnoręcznej implementacji mechanizmu kontroli wprowadzanych znaków (metody remove() oraz insertString() -- obydwie wywoływane są zanim napis w polu tekstowym ulegnie zmianie). Obiekt tej klasy przekazuje się do obiektu klasy JTextField za pomocą metody setDocument(). W ten sposób rozwiązano kwestię nadzoru nad wprowadzanymi przez użytkownika znakami.
    Kontrolę tego, czy wartość wprowadzonej liczby mieści się w zadanym przedziale zrealizowano przy użyciu klasy implementującej interfejs DocumentListener.


Kod źródłowy pliku "Swing1.java":

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;

public class Swing1 {
    public static void main(String[] args) {
        new Swing1();
    }

    private static final int MIN_VAL = 40;
    private static final int MAX_VAL = 400;
    private static final int MAX_NUM_OF_CHARS = 4;

    JTextField tf1 = null;

    public Swing1() {
        JFrame f = new JFrame("Swing1");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new FlowLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        tf1 = new JTextField(10);        
        MyDocument md = new MyDocument();
        tf1.setDocument(md);
        md.addDocumentListener(new MyDocumentListener());
        c.add(tf1);
        f.setVisible(true);        
    }

    class MyDocument extends PlainDocument {
        @Override
        public void remove(int ofs, int len)
                                            throws javax.swing.text.BadLocationException {
            System.out.println("MyDocument.remove(): tf1.getText() -> " + tf1.getText() +
                               ", ofs: " + ofs + ", len: " + len);

            super.remove(ofs, len);
        }

        @Override
        public void insertString(int ofs, String str, javax.swing.text.AttributeSet a)
                                            throws javax.swing.text.BadLocationException {
            System.out.println("MyDocument.insertString(): tf1.getText() -> " + tf1.getText() +
                               ", str: " + str + ", ofs: " + ofs);

            if (str == null)
                return;

            char[] t = new char[str.length()];
            int l = 0;
            for (int i = 0; i < str.length(); i++) {
                char c = str.charAt(i);
                if (Character.isDigit(c))
                   t[l++] = c;
            }

            if ((str.length() != l) || (ofs >= MAX_NUM_OF_CHARS)) {
                Toolkit.getDefaultToolkit().beep();
                l = 0;
            }

            super.insertString(ofs, new String(t, 0, l), a);
        }
    }

    class MyDocumentListener implements DocumentListener {
        public void changedUpdate(DocumentEvent e) { }

        public void insertUpdate(DocumentEvent e) {
            System.out.println("MyDocumentListener.insertUpdate(): tf1.getText() -> " +
                               tf1.getText());
            f();
        }

        public void removeUpdate(DocumentEvent e) {
            System.out.println("MyDocumentListener.removeUpdate(): tf1.getText() -> " +
                               tf1.getText());
            f();
        }

        private void f() {
            int val = 0;
            try {
                val = Integer.valueOf(tf1.getText());
            } catch (NumberFormatException e2) {
                tf1.setBackground(Color.decode("#ffb3b3"));
                return;
            }

            if ((val < MIN_VAL) || (val > MAX_VAL)) {
                tf1.setBackground(Color.decode("#ffb3b3"));
            } else
                tf1.setBackground(Color.decode("#bbffbb"));
        }
    }
}













Dla przykładowego wejścia (użytkownik wciskał kolejno 1, 2, 3, q, w, e, skopiował i wkleił "123", potem usuwał kolejno znaki) program wypisze:

MyDocument.insertString(): tf1.getText() -> , str: 1, ofs: 0
MyDocumentListener.insertUpdate(): tf1.getText() -> 1
MyDocument.insertString(): tf1.getText() -> 1, str: 2, ofs: 1
MyDocumentListener.insertUpdate(): tf1.getText() -> 12
MyDocument.insertString(): tf1.getText() -> 12, str: 3, ofs: 2
MyDocumentListener.insertUpdate(): tf1.getText() -> 123
MyDocument.insertString(): tf1.getText() -> 123, str: q, ofs: 3
MyDocument.insertString(): tf1.getText() -> 123, str: w, ofs: 3
MyDocument.insertString(): tf1.getText() -> 123, str: e, ofs: 3
MyDocument.insertString(): tf1.getText() -> 123, str: 123, ofs: 3
MyDocumentListener.insertUpdate(): tf1.getText() -> 123123
MyDocument.remove(): tf1.getText() -> 123123, ofs: 5, len: 1
MyDocumentListener.removeUpdate(): tf1.getText() -> 12312
MyDocument.remove(): tf1.getText() -> 12312, ofs: 4, len: 1
MyDocumentListener.removeUpdate(): tf1.getText() -> 1231
MyDocument.remove(): tf1.getText() -> 1231, ofs: 3, len: 1
MyDocumentListener.removeUpdate(): tf1.getText() -> 123
MyDocument.remove(): tf1.getText() -> 123, ofs: 2, len: 1
MyDocumentListener.removeUpdate(): tf1.getText() -> 12
MyDocument.remove(): tf1.getText() -> 12, ofs: 1, len: 1
MyDocumentListener.removeUpdate(): tf1.getText() -> 1
MyDocument.remove(): tf1.getText() -> 1, ofs: 0, len: 1
MyDocumentListener.removeUpdate(): tf1.getText() -> 

Widać, że metody klasy MyDocument są wywoływane zanim w polu tekstowym zajdzie zmiana napisu. Natomiast metody klasy MyDocumentListener są wywoływane po zajściu zmiany napisu w polu tekstowym. Metody klasy MyDocumentListener nie są wywoływane, gdy zawartość pola tekstowego nie ulegnie zmianie.

Zastanawiające jest to, czy kontroli wprowadzanych znaków (wymuszenia tego, aby można było wprowadzać wyłącznie cyfry) nie można dokonać w tej samej metodzie, w której sprawdzane jest, czy wartość wpisanej liczby mieści się w przedziale -- chodzi o metodę f() klasy MyDocumentListener. Teoretycznie można by modyfikować istniejącą zawartość pola tekstowego i usuwać zbędne znaki. Okazuje się, że nie ma takiej możliwości, gdyż próba użycia wyrażenia w rodzaju tf1.setText("!!!"); wewnątrz metody f() kończy się zgłoszeniem wyjątku java.lang.IllegalStateException: Attempt to mutate in notification. Można rozwiązać ten problem z użyciem invokeLater().

Jako rozwiązanie alternatywne mające na celu wymuszenie wprowadzania w polu tekstowym wyłącznie cyfr można było wybrać JFormattedTextField zamiast JTextField (P.S. aby uzyskać JFormattedTextField w edytorze GUI w NetBeans należy z palety wybrać Beans / Choose Bean, a następnie wpisać javax.swing.JFormattedTextField) wraz z formatowaniem (można tu stosować różnego rodzaju maski tak, aby wymusić odpowiedni format taki, jak np. data, czas, liczba itd.). Rozwiązanie przedstawiono poniżej.


Kod źródłowy pliku "Swing1b.java":

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;

public class Swing1b {
    public static void main(String[] args) {
        new Swing1b();
    }

    private static final int MIN_VAL = 40;
    private static final int MAX_VAL = 400;

    JFormattedTextField ftf1 = null;

    public Swing1b() {
        JFrame f = new JFrame("Swing1b");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new BorderLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        try {
            ftf1 = new JFormattedTextField(new MaskFormatter("####"));
        } catch (java.text.ParseException e) {
            System.exit(1);
        }

        ftf1.getDocument().addDocumentListener(new MyDocumentListener());
        c.add(ftf1);
        f.setVisible(true);        
    }

    class MyDocumentListener implements DocumentListener {
        public void changedUpdate(DocumentEvent e) { }

        public void insertUpdate(DocumentEvent e) {
            System.out.println("MyDocumentListener.insertUpdate(): ttf1.getText() -> " +
                               ftf1.getText());
            f();
        }

        public void removeUpdate(DocumentEvent e) {
            System.out.println("MyDocumentListener.removeUpdate(): ttf1.getText() -> " +
                               ftf1.getText());
            f();
        }

        private void f() {
            int val = 0;
            try {
                val = Integer.valueOf(ftf1.getText().trim());
            } catch (NumberFormatException e2) {
                ftf1.setBackground(Color.decode("#ffb3b3"));
                return;
            }

            if ((val < MIN_VAL) || (val > MAX_VAL)) {
                ftf1.setBackground(Color.decode("#ffb3b3"));
            } else
                ftf1.setBackground(Color.decode("#bbffbb"));
        }
    }
}



Swing 2

Temat: Pole tekstowe i suwak wzajemnie wpływające na swoje wartości


Kod źródłowy pliku "Swing2.java":

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;

public class Swing2 {
    public static void main(String[] args) {
        new Swing2();
    }

    private static final int MIN_VAL = 40;
    private static final int MAX_VAL = 400;

    JFormattedTextField ftf1 = null;
    JSlider sld1 = null;

    public Swing2() {
        JFrame f = new JFrame("Swing2");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new GridLayout(2, 1));
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        try {
            ftf1 = new JFormattedTextField(new MaskFormatter("####"));
        } catch (java.text.ParseException e) {
            System.exit(1);
        }
        ftf1.getDocument().addDocumentListener(new MyDocumentListener());
        ftf1.setFocusLostBehavior(javax.swing.JFormattedTextField.PERSIST);
        c.add(ftf1);

        sld1 = new JSlider(JSlider.HORIZONTAL, 0, 100, 60);
        sld1.setPaintTrack(true);
        sld1.setMinimum(MIN_VAL);
        sld1.setMaximum(MAX_VAL);
        sld1.addChangeListener(new ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                JSlider source = (JSlider)evt.getSource();
                try {
                   ftf1.setText("" + source.getValue());
                } catch (IllegalStateException e) { }
            }
        });
        c.add(sld1);
        f.setVisible(true);
    }

    class MyDocumentListener implements DocumentListener {
        public void changedUpdate(DocumentEvent e) { }

        public void insertUpdate(DocumentEvent e) {
            f();
        }

        public void removeUpdate(DocumentEvent e) {
            f();
        }

        private void f() {
            int val = 0;
            try {
                val = Integer.valueOf(ftf1.getText().trim());
            } catch (NumberFormatException e2) {
                ftf1.setBackground(Color.decode("#ffb3b3"));
                sld1.setValue(MIN_VAL);
                return;
            }
            if (val < MIN_VAL) {
                ftf1.setBackground(Color.decode("#ffb3b3"));
                sld1.setValue(MIN_VAL);
            } else if (val > MAX_VAL)  {
                ftf1.setBackground(Color.decode("#ffb3b3"));
                sld1.setValue(MAX_VAL);
            } else {
                ftf1.setBackground(Color.decode("#bbffbb"));
                sld1.setValue(val);
            }
        }
    }
}


















Program poniżej, to uproszczona wersja programu pierwotnego. Tym razem zastosowano JTextField, zamiast JFormattedTextField. Ponadto nie będzie sprawdzane, czy wpisana wartość należy do jakiegoś przedziału.


Kod źródłowy pliku "Swing2b.java":

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;

public class Swing2b {
    public static void main(String[] args) {
        new Swing2b();
    }

    private static final int MIN_VAL = 40;
    private static final int MAX_VAL = 400;

    JTextField tf1 = null;
    JSlider sld1 = null;

    public Swing2b() {
        JFrame f = new JFrame("Swing2b");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new GridLayout(2, 1));
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        tf1 = new JTextField(10);
        tf1.getDocument().addDocumentListener(new MyDocumentListener());
        c.add(tf1);

        sld1 = new JSlider(JSlider.HORIZONTAL, 0, 100, 60);
        sld1.setPaintTrack(true);
        sld1.setValue(MIN_VAL);
        sld1.setMinimum(MIN_VAL);
        sld1.setMaximum(MAX_VAL);
        sld1.addChangeListener(new ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                try {
                    tf1.setText("" + ((JSlider)evt.getSource()).getValue());
                    // tf1.setText("" + sld1.getValue()); <- to samo co wyzej,
                    // ale mniej uniwersalne w docelowych zastosowaniach
                } catch (IllegalStateException e) { }
            }
        });
        c.add(sld1);
        f.setVisible(true);
    }

    class MyDocumentListener implements DocumentListener {
        public void changedUpdate(DocumentEvent e) { }

        public void insertUpdate(DocumentEvent e) {
            f();
        }

        public void removeUpdate(DocumentEvent e) {
            f();
        }

        private void f() {
            int val = 0;
            try {
                val = Integer.valueOf(tf1.getText().trim());
            } catch (NumberFormatException e2) {
                sld1.setValue(MIN_VAL);
                return;
            }
            sld1.setValue(val);
        }
    }
}




Swing 3

Temat: Spinner bez możliwości ręcznego wpisywania wartości


Kod źródłowy pliku "Swing3.java":

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;

public class Swing3 {
    public static void main(String[] args) {
        new Swing3();
    }

    private static final int MIN_VAL = 10;
    private static final int MAX_VAL = 100;

    public Swing3() {
        JFrame f = new JFrame("Swing3");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new FlowLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JSpinner sp1 = new JSpinner();
        sp1.setModel(new javax.swing.SpinnerNumberModel(MIN_VAL, MIN_VAL, MAX_VAL, 1));
        JFormattedTextField ftf1 = ((JSpinner.DefaultEditor)sp1.getEditor()).getTextField();
        ftf1.setEditable(false);
        c.add(sp1);
        f.setVisible(true);
  }
}







Swing 4

Temat: Tabele

Za wyświetlanie oraz edycję danych w tabeli odpowiada kontrolka JTable. Jednak za przechowywanie danych odpowiedzialny jest obiekt klasy implementującej interfejs TableModel. W rzeczywistości nie ma potrzeby implementacji całego interfejsu TableModel; można stworzyć klasę dziedziczącą po klasie AbstractTableModel. Istnieje także, wywiedziona z klasy AbstractTableModel, klasa DefaultTableModel wyposażona w funkcjonalność przechowywania wartości komórek -- posiada takie użyteczne metody, jak: addColumn(), addRow(), getValueAt(), insertRow(), moveRow(), removeRow(), setValueAt() itp.

Poniżej zamieszczony kod implementuje prostą tabelę, bez nagłówka, bez możliwości przewijania zawartości dla dłuższych tabel, z możliwością zaznaczania wierszy oraz edycji komórek (wprowadzona wartość nie jest jednak zapamiętywana z powodu braku obsługi tej funkcjonalności w zamieszczonym przykładzie; ponadto nie byłoby nawet gdzie zapisać wprowadzonych danych); kolor siatki ustawiono na czerwony.


Kod źródłowy pliku "Swing4.java":

import javax.swing.*;
import java.awt.*;
import javax.swing.table.*;

public class Swing4 {
    public static void main(String[] args) {
        new Swing4();
    }

    private static final int COLS = 4;
    private static final int ROWS = 3;

    public Swing4() {
        JFrame f = new JFrame("Swing4");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new FlowLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        TableModel myDataModel = new AbstractTableModel() {
            public int getColumnCount() {
                return COLS;
            }
            public int getRowCount() {
                return ROWS;
            }
            public Object getValueAt(int row, int col) {
                return "(" + row + ", " + col + ")";
            }
            public boolean isCellEditable(int row, int col) {
                return true;
            }
        };

        JTable tbl1 = new JTable(myDataModel);
        tbl1.setGridColor(Color.red);

        c.add(tbl1);
        f.setVisible(true);
    }
}






Kolejny przykład demonstruje tabelę z nagłówkiem, z możliwością przewijania zawartości dla dłuższych tabel, z możliwością edycji komórek (wpisana wartość jest zapamiętywana), z możliwością zaznaczania. Zmieniono szerokość kolumny. Tym razem nie wskazano jawnie żadnego obiektu obsługującego model tabeli -- w takiej sytuacji JTable tworzy DefaultTableModel. Ponadto w poniższym przykładzie dane prezentowane w tabeli zapisane zostały w tablicy.


Kod źródłowy pliku "Swing4b.java":

import javax.swing.*;
import java.awt.*;

public class Swing4b {
    public static void main(String[] args) {
        new Swing4b();
    }

    public Swing4b() {
        JFrame f = new JFrame("Swing4b");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new BorderLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JTable tbl1 = new JTable(
            new Object [][] {
                { "Dennis Ritchie", "programista" },
                { "Albert Einstein", "fizyk" },
                { "Jacek Karpiński", "elektronik, informatyk" },
                { "Ken Thompson", "programista" },
                { "Wacław Sierpiński", "matematyk" },
            },
            new String [] {
                "nazwisko", "profesja"
            }
       );

        tbl1.setPreferredScrollableViewportSize(new Dimension(264, 70));
        tbl1.setFillsViewportHeight(true);

        tbl1.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
        tbl1.getColumnModel().getColumn(0).setMinWidth(105);
        tbl1.getColumnModel().getColumn(0).setMaxWidth(105);
        tbl1.getColumnModel().getColumn(0).setPreferredWidth(105);

        // na potrzeby przewijania zawartosci oraz wyswietlania naglowka
        JScrollPane scrp1 = new JScrollPane();
        scrp1.setViewportView(tbl1);

        c.add(scrp1);
        f.pack();
        f.setVisible(true);
    }
}






Poniżej zamieszczony przykład demonstruje tabelę z nagłówkiem, z możliwością przewijania zawartości dla dłuższych tabel, z możliwością edycji tylko komórek w jednej kolumnie oraz z podpowiedziami (tzw. ang. tooltips) dla pierwszej kolumny. Zmieniono szerokość kolumny.


Kod źródłowy pliku "Swing4c.java":

import javax.swing.*;
import java.awt.*;
import javax.swing.table.*;
import javax.swing.border.*;

public class Swing4c {
    public static void main(String[] args) {
        new Swing4c();
    }

    public Swing4c() {
        JFrame f = new JFrame("Swing4c");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new FlowLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JTable tbl1 = new JTable(new MyTableModel());

        MyTableCellRenderer rendered = new MyTableCellRenderer();
        tbl1.getColumnModel().getColumn(0).setCellRenderer(rendered);

        tbl1.setPreferredScrollableViewportSize(new Dimension(264, 70));
        tbl1.setFillsViewportHeight(true);

        tbl1.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
        tbl1.getColumnModel().getColumn(0).setMinWidth(105);
        tbl1.getColumnModel().getColumn(0).setMaxWidth(105);
        tbl1.getColumnModel().getColumn(0).setPreferredWidth(105);

        // na potrzeby przewijania zawartosci oraz wyswietlania naglowka
        JScrollPane scrp1 = new JScrollPane();
        scrp1.setViewportView(tbl1);

        c.add(scrp1);
        f.pack();
        f.setVisible(true);
    }


    class MyTableModel extends AbstractTableModel {
        private Object data[][] = {
                { "Dennis Ritchie", "programista" },
                { "Albert Einstein", "fizyk" },
                { "Jacek Karpiński", "elektronik, informatyk" },
                { "Ken Thompson", "programista" },
                { "Wacław Sierpiński", "matematyk" },
        };
        private String colsNames[] = { "nazwisko", "profesja" };

        public int getColumnCount() {
            return colsNames.length;
        }

        public String getColumnName(int col) {
            return colsNames[col];
        }

        public int getRowCount() {
            return data.length;
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }

        public boolean isCellEditable(int row, int col) {
            if (col == 1)
                return true;

            return false;
        }

        public void setValueAt(Object val, int row, int col) {
            data[row][col] = val;
            fireTableDataChanged();
        }
    }


    /* MyTableCellRenderer: aby umozliwic tooltipy dla komorek tabelki
     */
    class MyTableCellRenderer extends DefaultTableCellRenderer {
        private String tooltips[] = {
                "Twórca języka C oraz systemu operacyjnego UNIX",
                "Jeden z najwybitniejszych fizyków XX wieku",
                "Konstruktor pierwszych polskich komputerów",
                "Twórca systemu operacyjnego UNIX",
                "Wybitny polski matematyk"
        };

        public Component getTableCellRendererComponent(
                                JTable table, Object string,
                                boolean isSelected, boolean hasFocus,
                                int row, int col) {
            super.getTableCellRendererComponent(table, string, isSelected,
                                                hasFocus, row, col);

            setToolTipText((String)string + " - " + tooltips[row]);

            return this;
        }
    }
}









Tym razem dane znajdują się poza klasą MyTableModel. Teraz MyTableModel dziedziczy po DefaultTableModel. Nie można edytować zawartości komórek tabeli.


Kod źródłowy pliku "Swing4d.java":

import javax.swing.*;
import java.awt.*;
import javax.swing.table.*;
import javax.swing.border.*;

public class Swing4d {
    public static void main(String[] args) {
        new Swing4d();
    }

    Object data[][] = {
                { "Dennis Ritchie", "programista" },
                { "Albert Einstein", "fizyk" },
                { "Jacek Karpiński", "elektronik, informatyk" },
                { "Ken Thompson", "programista" },
                { "Wacław Sierpiński", "matematyk" },
    };

    public Swing4d() {
        JFrame f = new JFrame("Swing4d");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new FlowLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        MyTableModel myModel = new MyTableModel();
        myModel.setColumnIdentifiers(new String[] { "nazwisko", "profesja" });

        JTable tbl1 = new JTable(myModel);

        for (int i = 0; i < data.length; i++)
            myModel.addRow(data[i]);

        MyTableCellRenderer rendered = new MyTableCellRenderer();
        tbl1.getColumnModel().getColumn(0).setCellRenderer(rendered);

        tbl1.setPreferredScrollableViewportSize(new Dimension(264, 70));
        tbl1.setFillsViewportHeight(true);

        tbl1.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
        tbl1.getColumnModel().getColumn(0).setMinWidth(105);
        tbl1.getColumnModel().getColumn(0).setMaxWidth(105);
        tbl1.getColumnModel().getColumn(0).setPreferredWidth(105);

        // na potrzeby przewijania zawartosci oraz wyswietlania naglowka
        JScrollPane scrp1 = new JScrollPane();
        scrp1.setViewportView(tbl1);

        c.add(scrp1);
        f.pack();
        f.setVisible(true);
    }


    class MyTableModel extends DefaultTableModel {
        public boolean isCellEditable(int row, int col) {
            return false;
        }
    }


    /* MyTableCellRenderer: aby umozliwic tooltipy dla komorek tabelki
     */
    class MyTableCellRenderer extends DefaultTableCellRenderer {
        private String tooltips[] = {
                "Twórca języka C oraz systemu operacyjnego UNIX",
                "Jeden z najwybitniejszych fizyków XX wieku",
                "Konstruktor pierwszych polskich komputerów",
                "Twórca systemu operacyjnego UNIX",
                "Wybitny polski matematyk"
        };

        public Component getTableCellRendererComponent(
                                JTable table, Object string,
                                boolean isSelected, boolean hasFocus,
                                int row, int col) {
            super.getTableCellRendererComponent(table, string, isSelected,
                                                hasFocus, row, col);

            setToolTipText((String)string + " - " + tooltips[row]);

            return this;
        }
    }
}






Kolejna tabela ma ustawiony na sztywno rozmiar pierwszej kolumny (zresztą podobnie, jak miało to miejsce w niektórych wcześniejszych przykładach). Są także podpowiedzi (tzw. ang. tooltips) dla drugiej kolumny. Ponadto nie można edytować komórek, zaznaczać wierszy ani kolumn. Nie można także zamieniać kolejności kolumn.


Kod źródłowy pliku "Swing4e.java":

import javax.swing.*;
import java.awt.*;
import javax.swing.table.*;
import javax.swing.border.*;

public class Swing4e {
    public static void main(String[] args) {
        new Swing4e();
    }

    public Swing4e() {
        JFrame f = new JFrame("Swing4e");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new BorderLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JTable tbl1 = new JTable(new MyTableModel());

        MyTableCellRenderer rendered = new MyTableCellRenderer();
        tbl1.getColumnModel().getColumn(1).setCellRenderer(rendered);

        tbl1.getColumnModel().getColumn(0).setMinWidth(30);
        tbl1.getColumnModel().getColumn(0).setMaxWidth(30);
        tbl1.getColumnModel().getColumn(0).setPreferredWidth(30);

        tbl1.getTableHeader().setReorderingAllowed(false);
        tbl1.setCellSelectionEnabled(false);

        tbl1.setPreferredScrollableViewportSize(new Dimension(264, 70));
        tbl1.setFillsViewportHeight(true);

        // na potrzeby przewijania zawartosci oraz wyswietlania naglowka
        JScrollPane scrp1 = new JScrollPane();
        scrp1.setViewportView(tbl1);

        c.add(scrp1);
        f.pack();
        f.setVisible(true);
    }


    class MyTableModel extends AbstractTableModel {
        private Object data[] = {
                "Dennis Ritchie", "Albert Einstein", "Jacek Karpiński",
                "Ken Thompson", "Wacław Sierpiński" };

        private String colsNames[] = { "LP", "nazwisko" };

        public int getColumnCount() {
            return colsNames.length;
        }

        public String getColumnName(int col) {
            return colsNames[col];
        }

        public int getRowCount() {
            return data.length;
        }

        public Object getValueAt(int row, int col) {
            if (col == 0)
                return "" + (row + 1);
            else
                return data[row];
        }

        public boolean isCellEditable(int row, int col) {
            return false;
        }

    }


    /* MyTableCellRenderer: aby umozliwic tooltipy dla komorek tabelki
     */
    class MyTableCellRenderer extends DefaultTableCellRenderer {
        private String tooltips[] = {
                "Twórca języka C oraz systemu operacyjnego UNIX",
                "Jeden z najwybitniejszych fizyków XX wieku",
                "Konstruktor pierwszych polskich komputerów",
                "Twórca systemu operacyjnego UNIX",
                "Wybitny polski matematyk"
	};

        public Component getTableCellRendererComponent(
                                JTable table, Object string,
                                boolean isSelected, boolean hasFocus,
                                int row, int col) {
            super.getTableCellRendererComponent(table, string, isSelected,
                                                hasFocus, row, col);

            setToolTipText((String)string + " - " + tooltips[row]);

            return this;
        }
    }
}










Swing 5

Temat: JSplitPane

Celem poniżej zamieszczonego programu było uzyskanie JSplitPane, w którym to dolny panel ma stałą, niezmienną wysokość, użytkownik nie może zmieniać pozycji pozycjonera dzielącego panele, użytkownik może chować i pokazywać dolny panel, ale nie może schować panela górnego.


Kod źródłowy pliku "Swing5.java":

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.plaf.basic.*;

public class Swing5 {
    public static void main(String[] args) {
        new Swing5();
    }

    public Swing5() {
        JFrame f = new JFrame("Swing5");
        f.setSize(300, 100);
        Container c = f.getContentPane();
        c.setLayout(new BorderLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel p1 = new JPanel();
        p1.setPreferredSize(new Dimension(300, 100));

        JPanel p2 = new JPanel();
        p2.setMinimumSize(new Dimension(0, 50));
        p2.setMaximumSize(new Dimension(0, 50));
        p2.setPreferredSize(new Dimension(300, 50));

        JSplitPane sp1 = new JSplitPane();
        sp1.setDividerSize(15);
        sp1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
        sp1.setResizeWeight(1.0);
        sp1.setOneTouchExpandable(true);
        sp1.setTopComponent(p1);
        sp1.setBottomComponent(p2);

        sp1.setUI(new BasicSplitPaneUI() {
            public BasicSplitPaneDivider createDefaultDivider() {
                return new BasicSplitPaneDivider(this) {

                    public void setBasicSplitPaneUI(BasicSplitPaneUI newUI) {
                        super.setBasicSplitPaneUI(newUI);

                        leftButton.setEnabled(false);

                        leftButton.addActionListener(new ActionListener() {
                            public void actionPerformed(ActionEvent e) {
                                if (!leftButton.isEnabled())
                                    return;
                                leftButton.setEnabled(false);
                                SwingUtilities.invokeLater(new Runnable() {
                                    public void run() {
                                        splitPane.resetToPreferredSizes();
                                    }
                                });
                            }
                        });

                        rightButton.addActionListener(new ActionListener() {
                            public void actionPerformed(ActionEvent e) {
                                leftButton.setEnabled(true);
                            }
                        });

                        splitPane.removePropertyChangeListener(this);
                        if (mouseHandler != null) {
                            splitPane.removeMouseListener(mouseHandler);
                            splitPane.removeMouseMotionListener(mouseHandler);
                            removeMouseListener(mouseHandler);
                            removeMouseMotionListener(mouseHandler);
                        }
                        setCursor(java.awt.Cursor.getPredefinedCursor(
                                            java.awt.Cursor.DEFAULT_CURSOR));
                    }
                };
            }
        });

        c.add(sp1);
        f.pack();
        f.setVisible(true);
    }
}












w3cw3c
automatyka przemysłowa