Улучшаем интерфейс - листенеры, модель таблицы и потокиПеределав наше приложения в плане работы с базой данных, вернемся к пользовательскому интерфейсу. Во-первых мы не реализовали достаточно много функций, во-вторых - наш интерфейс вообщем-то далек от совершенства.
Предварительно опишем (еще раз), что мы теперь хотим получить:
- В верхней части экрана у нас остается тот же самый спин. Но к нему надо приделать листенер, который будет вызывать обновление списка студентов.
- Список групп. Мы его оставим таким, как был. Только добавим листенер, который будет реагировать на изменение выделенной группы.
- Список студентов будет изменен. Во-первых будем показывать студентов из выделенной группы, а во-вторых будем показывать их не списком, а таблицей. Для таблицы мы создадим свю собственную модель. Особенности мы обсудим позже.
- Добавим три кнопки для редактирования студентов в нижнюю часть таблицы со списком студентов для редактирования
- Добавим две кнопки внизу списка групп - для удаления всех студентов из группы и для перевода всех студентов в другую группу. Хорошо наверно было бы сделать возможность выделять студентов и переводить только выделенных, но это мы оставим для самостоятельного изучения. Кроме этого мы сделаем наш список групп постоянным по ширине - иначе, если группы называются 1, 2, 3 и т.д., то наш список будет очень узким.
- При добавлении нового студента или редактировании существующего будем выводить диалоговое окно. При добавлении студента диалоговое окно не должно закрываться после добавления студента - пользователь может сразу начать вводить данные другого студента. При этом значения для полей ГРУППА и ГОД должны оставаться такими, как были, а все остальные поля очищаются. Конечно же группы выбираются не по ИД, а из списка.
- Добавим пункты меню "Отчеты", где мы сможем получать различные отчеты. На сегодня у нас только один отчет - список всех студентов.
Теперь давайте постепенно будем приближаться к нашей цели. Стоит отметить, что наш класс ManagementSystem делает все, что нам надо и то, что касается так называемого back-end (такое название относится к той части системы, которая отвечает за работу с базой данных) нас уже мало волнует - все для работы с базой у нас есть. Мы будем просто вызывать те методы, которые нам потребуются.
Итак, давайте сделаем первую версию нашего интерфейса. Мы добавим кнопки внизу и сделаем нашу панель для групп не сжимаемую по ширине. Кнопки можно увидеть в тексте - там ничего нет сложного.
Для "несжимаемости" нам надо будет переопределить метод getPreferredSize(). Его можно увидеть в коде.
Мы пока будем смотреть на один файл - StudentsFrame.java. Все остальные не будут изменяться совсем. Как обычно в конце статьи будут приведены коды для всех файлов
Код |
package students.frame;
import java.sql.SQLException; import java.util.Vector;
import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.BorderLayout; import java.awt.GridLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable;
import javax.swing.JSpinner; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.border.BevelBorder;
import students.logic.ManagementSystem;
public class StudentsFrame extends JFrame { // Введем сразу имена для кнопок - потом будем их использовать в обработчиках private static final String MOVE_GR = "moveGroup"; private static final String CLEAR_GR = "clearGroup"; private static final String INSERT_ST = "insertStudent"; private static final String UPDATE_ST = "updateStudent"; private static final String DELETE_ST = "deleteStudent"; private static final String ALL_STUDENTS = "allStudent"; ManagementSystem ms = ManagementSystem.getInstance(); private JList grpList; private JTable stdList; private JSpinner spYear; public StudentsFrame() { // Устанавливаем layout для всей клиентской части формы getContentPane().setLayout(new BorderLayout());
// Создаем строку меню JMenuBar menuBar = new JMenuBar(); // Создаем выпадающее меню JMenu menu = new JMenu("Отчеты"); // Создаем пункт в выпадающем меню JMenuItem menuItem = new JMenuItem("Все студенты"); menuItem.setName(ALL_STUDENTS); // Вставляем пункт меню в выпадающее меню menu.add(menuItem); // Вставляем выпадающее меню в строку меню menuBar.add(menu); // Устанавливаем меню для формы setJMenuBar(menuBar); // Создаем верхнюю панель, где будет поле для ввода года JPanel top = new JPanel(); // Устанавливаем для нее layout top.setLayout(new FlowLayout(FlowLayout.LEFT));
// Вставляем пояснительную надпись top.add(new JLabel("Год обучения:")); // Делаем спин-поле // 1. Задаем модель поведения - только цифры // 2. Вставляем в панель SpinnerModel sm = new SpinnerNumberModel(2006, 1900, 2100, 1); spYear = new JSpinner(sm); top.add(spYear);
// Создаем нижнюю панель и задаем ей layout JPanel bot = new JPanel(); bot.setLayout(new BorderLayout());
// Создаем левую панель для вывода списка групп // Она у нас GroupPanel left = new GroupPanel(); // Задаем layout и задаем "бордюр" вокруг панели left.setLayout(new BorderLayout()); left.setBorder(new BevelBorder(BevelBorder.LOWERED));
// Нам необходимо обработать ошибку при обращении к базе данных Vector gr = null; try { // Получаем список групп gr = new Vector(ms.getGroups()); } catch (SQLException e) { e.printStackTrace(); } // Создаем надпись left.add(new JLabel("Группы:"), BorderLayout.NORTH); // Создаем визуальный список и вставляем его в скроллируемую // панель, которую в свою очередь уже кладем на панель left grpList = new JList(gr); left.add(new JScrollPane(grpList), BorderLayout.CENTER); // Создаем кнопки для групп JButton btnMvGr = new JButton("Переместить"); btnMvGr.setName(MOVE_GR); JButton btnClGr = new JButton("Очистить"); btnClGr.setName(CLEAR_GR); // Создаем панель, на которую положим наши кнопки и кладем ее вниз JPanel pnlBtnGr = new JPanel(); pnlBtnGr.setLayout(new GridLayout(1,2)); pnlBtnGr.add(btnMvGr); pnlBtnGr.add(btnClGr); left.add(pnlBtnGr, BorderLayout.SOUTH);
// Создаем правую панель для вывода списка студентов JPanel right = new JPanel(); // Задаем layout и задаем "бордюр" вокруг панели right.setLayout(new BorderLayout()); right.setBorder(new BevelBorder(BevelBorder.LOWERED));
// Создаем надпись right.add(new JLabel("Студенты:"), BorderLayout.NORTH); // Создаем таблицу и вставляем ее в скроллируемую // панель, которую в свою очередь уже кладем на панель right // Наша таблица пока ничего не умеет - просто положим ее как заготовку // Сделаем в ней 4 колонки - Фамилия, Имя, Отчество, Дата рождения stdList = new JTable(1, 4); right.add(new JScrollPane(stdList), BorderLayout.CENTER); // Создаем кнопки для студентов JButton btnAddSt = new JButton("Добавить"); btnAddSt.setName(INSERT_ST); JButton btnUpdSt = new JButton("Исправить"); btnUpdSt.setName(UPDATE_ST); JButton btnDelSt = new JButton("Удалить"); btnDelSt.setName(DELETE_ST); // Создаем панель, на которую положим наши кнопки и кладем ее вниз JPanel pnlBtnSt = new JPanel(); pnlBtnSt.setLayout(new GridLayout(1,3)); pnlBtnSt.add(btnAddSt); pnlBtnSt.add(btnUpdSt); pnlBtnSt.add(btnDelSt); right.add(pnlBtnSt, BorderLayout.SOUTH);
// Вставляем панели со списками групп и студентов в нижнюю панель bot.add(left, BorderLayout.WEST); bot.add(right, BorderLayout.CENTER);
// Вставляем верхнюю и нижнюю панели в форму getContentPane().add(top, BorderLayout.NORTH); getContentPane().add(bot, BorderLayout.CENTER);
// Сразу выделяем первую группу grpList.setSelectedIndex(0);
// Задаем границы формы setBounds(100, 100, 700, 500); }
public static void main(String args[]) { StudentsFrame sf = new StudentsFrame(); sf.setDefaultCloseOperation(EXIT_ON_CLOSE); sf.setVisible(true); } }
// Наш внутренний класс - переопределенная панель. class GroupPanel extends JPanel {
public Dimension getPreferredSize() { return new Dimension(250, 0); } }
|
Теперь мы сделаем так, чтобы наш интерфей "ожил". Сделаем реакции на кнопки,изменения групп и спинера. Будем вызывать методы, в которых будут стоять "заглушки" - сообщения о том, что метод вызван.
У нас сообщения приходят от четырех видов компонентов - меню, кнопка, спинер, список.
Для меню и кнопок используется один вид листенера - ActionListener.
Для списка используется, как мы уже знаем, ListSelectionListener.
Остается только спинер - для него используется ChangeListener.
Давайте теперь расставим все наши листенеры и сделаем "заглушки". Можно запустить наше приложение и проверить все наши управляющие компоненты - по идее все должно работать, т.е. выдавать сообщения на каждое действие - меню, кнопки, перемещение по списку групп, изменение величины в спинере. Комментарии к коду смотрите прямо в тексте программы.
Код |
package students.frame;
import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.sql.SQLException; import java.util.Vector;
import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.border.BevelBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener;
import students.logic.ManagementSystem;
public class StudentsFrame extends JFrame implements ActionListener, ListSelectionListener, ChangeListener { // Введем сразу имена для кнопок - потом будем их использовать в обработчиках private static final String MOVE_GR = "moveGroup"; private static final String CLEAR_GR = "clearGroup"; private static final String INSERT_ST = "insertStudent"; private static final String UPDATE_ST = "updateStudent"; private static final String DELETE_ST = "deleteStudent"; private static final String ALL_STUDENTS = "allStudent";
ManagementSystem ms = ManagementSystem.getInstance(); private JList grpList; private JTable stdList; private JSpinner spYear;
public StudentsFrame() { // Устанавливаем layout для всей клиентской части формы getContentPane().setLayout(new BorderLayout());
// Создаем строку меню JMenuBar menuBar = new JMenuBar(); // Создаем выпадающее меню JMenu menu = new JMenu("Отчеты"); // Создаем пункт в выпадающем меню JMenuItem menuItem = new JMenuItem("Все студенты"); menuItem.setName(ALL_STUDENTS); // Добавляем листенер menuItem.addActionListener(this); // Вставляем пункт меню в выпадающее меню menu.add(menuItem); // Вставляем выпадающее меню в строку меню menuBar.add(menu); // Устанавливаем меню для формы setJMenuBar(menuBar);
// Создаем верхнюю панель, где будет поле для ввода года JPanel top = new JPanel(); // Устанавливаем для нее layout top.setLayout(new FlowLayout(FlowLayout.LEFT));
// Вставляем пояснительную надпись top.add(new JLabel("Год обучения:")); // Делаем спин-поле // 1. Задаем модель поведения - только цифры // 2. Вставляем в панель SpinnerModel sm = new SpinnerNumberModel(2006, 1900, 2100, 1); spYear = new JSpinner(sm); // Добавляем листенер spYear.addChangeListener(this); top.add(spYear);
// Создаем нижнюю панель и задаем ей layout JPanel bot = new JPanel(); bot.setLayout(new BorderLayout());
// Создаем левую панель для вывода списка групп // Она у нас GroupPanel left = new GroupPanel(); // Задаем layout и задаем "бордюр" вокруг панели left.setLayout(new BorderLayout()); left.setBorder(new BevelBorder(BevelBorder.LOWERED));
// Нам необходимо обработать ошибку при обращении к базе данных Vector gr = null; try { // Получаем список групп gr = new Vector(ms.getGroups()); } catch (SQLException e) { e.printStackTrace(); } // Создаем надпись left.add(new JLabel("Группы:"), BorderLayout.NORTH); // Создаем визуальный список и вставляем его в скроллируемую // панель, которую в свою очередь уже кладем на панель left grpList = new JList(gr); // Добавляем листенер grpList.addListSelectionListener(this); // Сразу выделяем первую группу grpList.setSelectedIndex(0); left.add(new JScrollPane(grpList), BorderLayout.CENTER); // Создаем кнопки для групп JButton btnMvGr = new JButton("Переместить"); btnMvGr.setName(MOVE_GR); JButton btnClGr = new JButton("Очистить"); btnClGr.setName(CLEAR_GR); // Добавляем листенер btnMvGr.addActionListener(this); btnClGr.addActionListener(this); // Создаем панель, на которую положим наши кнопки и кладем ее вниз JPanel pnlBtnGr = new JPanel(); pnlBtnGr.setLayout(new GridLayout(1, 2)); pnlBtnGr.add(btnMvGr); pnlBtnGr.add(btnClGr); left.add(pnlBtnGr, BorderLayout.SOUTH);
// Создаем правую панель для вывода списка студентов JPanel right = new JPanel(); // Задаем layout и задаем "бордюр" вокруг панели right.setLayout(new BorderLayout()); right.setBorder(new BevelBorder(BevelBorder.LOWERED));
// Создаем надпись right.add(new JLabel("Студенты:"), BorderLayout.NORTH); // Создаем таблицу и вставляем ее в скроллируемую // панель, которую в свою очередь уже кладем на панель right // Наша таблица пока ничего не умеет - просто положим ее как заготовку // Сделаем в ней 4 колонки - Фамилия, Имя, Отчество, Дата рождения stdList = new JTable(1, 4); right.add(new JScrollPane(stdList), BorderLayout.CENTER); // Создаем кнопки для студентов JButton btnAddSt = new JButton("Добавить"); btnAddSt.setName(INSERT_ST); btnAddSt.addActionListener(this); JButton btnUpdSt = new JButton("Исправить"); btnUpdSt.setName(UPDATE_ST); btnUpdSt.addActionListener(this); JButton btnDelSt = new JButton("Удалить"); btnDelSt.setName(DELETE_ST); btnDelSt.addActionListener(this); // Создаем панель, на которую положим наши кнопки и кладем ее вниз JPanel pnlBtnSt = new JPanel(); pnlBtnSt.setLayout(new GridLayout(1, 3)); pnlBtnSt.add(btnAddSt); pnlBtnSt.add(btnUpdSt); pnlBtnSt.add(btnDelSt); right.add(pnlBtnSt, BorderLayout.SOUTH);
// Вставляем панели со списками групп и студентов в нижнюю панель bot.add(left, BorderLayout.WEST); bot.add(right, BorderLayout.CENTER);
// Вставляем верхнюю и нижнюю панели в форму getContentPane().add(top, BorderLayout.NORTH); getContentPane().add(bot, BorderLayout.CENTER);
// Задаем границы формы setBounds(100, 100, 700, 500); }
public static void main(String args[]) { StudentsFrame sf = new StudentsFrame(); sf.setDefaultCloseOperation(EXIT_ON_CLOSE); sf.setVisible(true); }
// Метод для обеспечения интерфейса ActionListener public void actionPerformed(ActionEvent e) { if (e.getSource() instanceof Component) { Component c = (Component) e.getSource(); if (c.getName().equals(MOVE_GR)) { moveGroup(); } if (c.getName().equals(CLEAR_GR)) { clearGroup(); } if (c.getName().equals(ALL_STUDENTS)) { showAllStudents(); } if (c.getName().equals(INSERT_ST)) { insertStudent(); } if (c.getName().equals(UPDATE_ST)) { updateStudent(); } if (c.getName().equals(DELETE_ST)) { deleteStudent(); } } }
// Метод для обеспечения интерфейса ListSelectionListener public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { reloadStudents(); } }
// Метод для обеспечения интерфейса ChangeListener public void stateChanged(ChangeEvent e) { reloadStudents(); }
// метод для обновления списка студентов для определенной группы private void reloadStudents() { JOptionPane.showMessageDialog(this, "reloadStudents"); }
// метод для переноса группы private void moveGroup() { JOptionPane.showMessageDialog(this, "moveGroup"); }
// метод для очистки группы private void clearGroup() { JOptionPane.showMessageDialog(this, "clearGroup"); }
// метод для добавления студента private void insertStudent() { JOptionPane.showMessageDialog(this, "insertStudent"); }
// метод для редактирования студента private void updateStudent() { JOptionPane.showMessageDialog(this, "updateStudent"); }
// метод для удаления студента private void deleteStudent() { JOptionPane.showMessageDialog(this, "deleteStudent"); }
// метод для показа всех студентов private void showAllStudents() { JOptionPane.showMessageDialog(this, "showAllStudents"); } }
// Наш внутренний класс - переопределенная панель. class GroupPanel extends JPanel {
public Dimension getPreferredSize() { return new Dimension(250, 0); } }
|
Модель для таблицыТеперь давайте реализуем первую команду - перегрузка списка студентов. Как уже упоминалось выше, нам предстоит определить свою модель. Реализация полной модели, т.е. интерфейса TableModel - задача сложная. В большинстве случаев нам необходимо только реализовать несколько методов. Понимая это разработчики JAVA создали класс AbstractTableModel, который реализует большинство необходимых методов. Для создания своей модели достаточно переопределить всего 3 метода:
- public int getRowCount();
- public int getColumnCount();
- public Object getValueAt(int row, int column);
Если мы запишем список студентов в вектор и определим порядок столбцов, то реализация будет выглядеть достаточно несложно.
Конечно, существует возможность использовать таблицу через стандартные вызовы отдавая ей векторы. Но могу Вас уверить - это будет выглядеть сожнее, чем написать свою модель. Тем более, что бОльшая часть работы уже сделана за нас - классом AbstractTableModel
StudentTableModel.java
Код |
package students.frame;
import java.text.DateFormat; import java.util.Vector;
import javax.swing.table.AbstractTableModel;
import students.logic.Student;
public class StudentTableModel extends AbstractTableModel { // Сделаем хранилище для нашего списка студентов private Vector students;
// Модель при создании получает список студентов public StudentTableModel(Vector students) { this.students = students; }
// Количество строк равно числу записей public int getRowCount() { if (students != null) { return students.size(); } return 0; }
// Количество столбцов - 4. Фамилия, Имя, Отчество, Дата рождения public int getColumnCount() { return 4; }
// Вернем наименование колонки public String getColumnName(int column) { String[] colNames = { "Фамилия", "Имя", "Отчество", "Дата" }; return colNames[column]; }
// Возвращаем данные для определенной строки и столбца public Object getValueAt(int rowIndex, int columnIndex) { if (students != null) { // Получаем из вектора студента Student st = (Student) students.get(rowIndex); // В зависимости от колонки возвращаем имя, фамилия и т.д. switch (columnIndex) { case 0: return st.getSurName(); case 1: return st.getFirstName(); case 2: return st.getPatronymic(); case 3: return DateFormat.getDateInstance(DateFormat.SHORT).format( st.getDateOfBirth()); } } return null; }
// Добавим метод, который возвращает студента по номеру строки // Это нам пригодится чуть позже public Student getStudent(int rowIndex) { if(students!=null) { if(rowIndex<students.size() && rowIndex>=0) { return (Student)students.get(rowIndex); } } return null; } }
|
И теперь можно привести код для обновленного StudentsFrame - там сделано важное изменение - реализован метод reloadStudents(), который загружает список студентов для выделенной группы и года
Код |
package students.frame;
import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.sql.SQLException; import java.util.Collection; import java.util.Vector;
import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.border.BevelBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener;
import students.logic.Group; import students.logic.ManagementSystem;
public class StudentsFrame extends JFrame implements ActionListener, ListSelectionListener, ChangeListener { // Введем сразу имена для кнопок - потом будем их использовать в обработчиках private static final String MOVE_GR = "moveGroup"; private static final String CLEAR_GR = "clearGroup"; private static final String INSERT_ST = "insertStudent"; private static final String UPDATE_ST = "updateStudent"; private static final String DELETE_ST = "deleteStudent"; private static final String ALL_STUDENTS = "allStudent";
ManagementSystem ms = ManagementSystem.getInstance(); private JList grpList; private JTable stdList; private JSpinner spYear;
private JMenuBar menuBar = new JMenuBar();
public StudentsFrame() { // Устанавливаем layout для всей клиентской части формы getContentPane().setLayout(new BorderLayout());
// Создаем строку меню JMenuBar menuBar = new JMenuBar(); // Создаем выпадающее меню JMenu menu = new JMenu("Отчеты"); // Создаем пункт в выпадающем меню JMenuItem menuItem = new JMenuItem("Все студенты"); menuItem.setName(ALL_STUDENTS); // Добавляем листенер menuItem.addActionListener(this); // Вставляем пункт меню в выпадающее меню menu.add(menuItem); // Вставляем выпадающее меню в строку меню menuBar.add(menu); // Устанавливаем меню для формы setJMenuBar(menuBar);
// Создаем верхнюю панель, где будет поле для ввода года JPanel top = new JPanel(); // Устанавливаем для нее layout top.setLayout(new FlowLayout(FlowLayout.LEFT));
// Вставляем пояснительную надпись top.add(new JLabel("Год обучения:")); // Делаем спин-поле // 1. Задаем модель поведения - только цифры // 2. Вставляем в панель SpinnerModel sm = new SpinnerNumberModel(2006, 1900, 2100, 1); spYear = new JSpinner(sm); // Добавляем листенер spYear.addChangeListener(this); top.add(spYear);
// Создаем нижнюю панель и задаем ей layout JPanel bot = new JPanel(); bot.setLayout(new BorderLayout());
// Создаем левую панель для вывода списка групп // Она у нас GroupPanel left = new GroupPanel(); // Задаем layout и задаем "бордюр" вокруг панели left.setLayout(new BorderLayout()); left.setBorder(new BevelBorder(BevelBorder.LOWERED));
// Нам необходимо обработать ошибку при обращении к базе данных Vector gr = null; try { // Получаем список групп gr = new Vector(ms.getGroups()); } catch (SQLException e) { e.printStackTrace(); } // Создаем надпись left.add(new JLabel("Группы:"), BorderLayout.NORTH); // Создаем визуальный список и вставляем его в скроллируемую // панель, которую в свою очередь уже кладем на панель left grpList = new JList(gr); // Добавляем листенер grpList.addListSelectionListener(this); left.add(new JScrollPane(grpList), BorderLayout.CENTER); // Создаем кнопки для групп JButton btnMvGr = new JButton("Переместить"); btnMvGr.setName(MOVE_GR); JButton btnClGr = new JButton("Очистить"); btnClGr.setName(CLEAR_GR); // Добавляем листенер btnMvGr.addActionListener(this); btnClGr.addActionListener(this); // Создаем панель, на которую положим наши кнопки и кладем ее вниз JPanel pnlBtnGr = new JPanel(); pnlBtnGr.setLayout(new GridLayout(1, 2)); pnlBtnGr.add(btnMvGr); pnlBtnGr.add(btnClGr); left.add(pnlBtnGr, BorderLayout.SOUTH);
// Создаем правую панель для вывода списка студентов JPanel right = new JPanel(); // Задаем layout и задаем "бордюр" вокруг панели right.setLayout(new BorderLayout()); right.setBorder(new BevelBorder(BevelBorder.LOWERED));
// Создаем надпись right.add(new JLabel("Студенты:"), BorderLayout.NORTH); // Создаем таблицу и вставляем ее в скроллируемую // панель, которую в свою очередь уже кладем на панель right // Наша таблица пока ничего не умеет - просто положим ее как заготовку // Сделаем в ней 4 колонки - Фамилия, Имя, Отчество, Дата рождения stdList = new JTable(1, 4); right.add(new JScrollPane(stdList), BorderLayout.CENTER); // Создаем кнопки для студентов JButton btnAddSt = new JButton("Добавить"); btnAddSt.setName(INSERT_ST); btnAddSt.addActionListener(this); JButton btnUpdSt = new JButton("Исправить"); btnUpdSt.setName(UPDATE_ST); btnUpdSt.addActionListener(this); JButton btnDelSt = new JButton("Удалить"); btnDelSt.setName(DELETE_ST); btnDelSt.addActionListener(this); // Создаем панель, на которую положим наши кнопки и кладем ее вниз JPanel pnlBtnSt = new JPanel(); pnlBtnSt.setLayout(new GridLayout(1, 3)); pnlBtnSt.add(btnAddSt); pnlBtnSt.add(btnUpdSt); pnlBtnSt.add(btnDelSt); right.add(pnlBtnSt, BorderLayout.SOUTH);
// Вставляем панели со списками групп и студентов в нижнюю панель bot.add(left, BorderLayout.WEST); bot.add(right, BorderLayout.CENTER);
// Вставляем верхнюю и нижнюю панели в форму getContentPane().add(top, BorderLayout.NORTH); getContentPane().add(bot, BorderLayout.CENTER);
// Сразу выделяем первую группу grpList.setSelectedIndex(0);
// Задаем границы формы setBounds(100, 100, 700, 500); }
public static void main(String args[]) { StudentsFrame sf = new StudentsFrame(); sf.setDefaultCloseOperation(EXIT_ON_CLOSE); sf.setVisible(true); }
// Метод для обеспечения интерфейса ActionListener public void actionPerformed(ActionEvent e) { if (e.getSource() instanceof Component) { Component c = (Component) e.getSource(); if (c.getName().equals(MOVE_GR)) { moveGroup(); } if (c.getName().equals(CLEAR_GR)) { clearGroup(); } if (c.getName().equals(ALL_STUDENTS)) { showAllStudents(); } if (c.getName().equals(INSERT_ST)) { insertStudent(); } if (c.getName().equals(UPDATE_ST)) { updateStudent(); } if (c.getName().equals(DELETE_ST)) { deleteStudent(); } } }
// Метод для обеспечения интерфейса ListSelectionListener public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { reloadStudents(); } }
// Метод для обеспечения интерфейса ChangeListener public void stateChanged(ChangeEvent e) { reloadStudents(); }
// метод для обновления списка студентов для определенной группы private void reloadStudents() { if (stdList != null) { // Получаем выделенную группу Group g = (Group) grpList.getSelectedValue(); // Получаем число из спинера int y = ((SpinnerNumberModel) spYear.getModel()).getNumber().intValue(); try { // Получаем список студентов Collection s = ms.getStudentsFromGroup(g, y); // И устанавливаем модель для таблицы с новыми данными stdList.setModel(new StudentTableModel(new Vector(s))); } catch (SQLException e) { JOptionPane.showMessageDialog(StudentsFrame.this, e.getMessage()); } } }
// метод для переноса группы private void moveGroup() { JOptionPane.showMessageDialog(this, "moveGroup"); }
// метод для очистки группы private void clearGroup() { JOptionPane.showMessageDialog(this, "clearGroup"); }
// метод для добавления студента private void insertStudent() { JOptionPane.showMessageDialog(this, "insertStudent"); }
// метод для редактирования студента private void updateStudent() { JOptionPane.showMessageDialog(this, "updateStudent"); }
// метод для удаления студента private void deleteStudent() { JOptionPane.showMessageDialog(this, "deleteStudent"); }
// метод для показа всех студентов private void showAllStudents() { JOptionPane.showMessageDialog(this, "showAllStudents"); } }
// Наш внутренний класс - переопределенная панель. class GroupPanel extends JPanel {
public Dimension getPreferredSize() { return new Dimension(250, 0); } }
|
Кажется, что все замечательно - наша программа научилась обновлять список студентов и все смотрится красиво. Но есть одна неприятность. Представьте себе, что список студентов по какой-либо причине не может быть обновлен быстро. Проведите эксперимент - замените метод reloadStudents() на вот такой код:
Код |
// метод для обновления списка студентов для определенной группы private void reloadStudents() { if (stdList != null) { // Получаем выделенную группу Group g = (Group) grpList.getSelectedValue(); // Получаем число из спинера int y = ((SpinnerNumberModel) spYear.getModel()).getNumber().intValue(); try { // Получаем список студентов Collection s = ms.getStudentsFromGroup(g, y); // И устанавливаем модель для таблицы с новыми данными stdList.setModel(new StudentTableModel(new Vector(s))); } catch (SQLException e) { JOptionPane.showMessageDialog(StudentsFrame.this, e.getMessage()); } } // Вводим искусственную задержку на 3 секунды try { Thread.sleep(3000); } catch(Exception e) {} }
|
После этого запустите пример (подождите 3 секунды - форма появиться не сразу) и попробуйте нажать на стрелочку спинера. Как Вам эффект ? Спинер завис, все приложение не реагирует - крайне неприятная ситуация.
Причем можно заметить, что несмотря на то, что мы уже обновили модель изменения не видны. Мы вынуждены ждать
Если погрузиться чуть глубже в систему рисования SWING, то Вы узнаете, что прорисовка компонентов идет в отдельном потоке (треде) и, что самое неприятное, именно в этом же треде вызываются методы листенеров. Т.е. мы исправили модель, но т.к. наш метод еще не завершился обновление экрана не произошло. И, как Вы сами понимаете, зависание какого-либо метода внутри обработки выглядит ужасно.
Поэтому очень хорошим выходом из данной ситуации может служить многопотоковость. О потоках вы можете прочитать в документации, либо посмотреть очень приличную статью в нашем FAQ "Многопоточное программирование" -
--------------------