Книга 1 - Начальные сведения
Книга 2 - Более профессиональный подход |
Студенческий отдел кадров Улучшаем интерфейс - листенеры, модель таблицы и потокиПеределав наше приложения в плане работы с базой данных, вернемся к пользовательскому интерфейсу. Во-первых, мы не реализовали достаточно много функций, во-вторых - наш интерфейс в общем-то далек от совершенства. Предварительно опишем (еще раз), что мы теперь хотим получить:
Итак, давайте сделаем первую версию нашего интерфейса. Мы добавим кнопки внизу и сделаем нашу панель для групп не сжимаемую по ширине. Кнопки можно увидеть в тексте - там ничего нет сложного. Для "несжимаемости" нам надо будет переопределить метод getPreferredSize(). Его можно увидеть в коде. Мы пока будем смотреть на один файл - StudentsFrame.java. Все остальные не будут изменяться совсем. Как обычно в конце статьи будут приведены тексты для всех файлов. 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.SwingUtilities; 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"; private ManagementSystem ms = null; private JList grpList; private JTable stdList; private JSpinner spYear; public StudentsFrame() throws Exception { // Устанавливаем 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)); // Получаем коннект к базе и создаем объект ManagementSystem ms = ManagementSystem.getInstance(); // Получаем список групп Vector<Group> gr = new Vector<Group>(ms.getGroups()); // Создаем надпись 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[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { try { // Мы сразу отменим продолжение работы, если не сможем получить // коннект к базе данных StudentsFrame sf = new StudentsFrame(); sf.setDefaultCloseOperation(EXIT_ON_CLOSE); sf.setVisible(true); } catch (Exception ex) { ex.printStackTrace(); } } }); } } // Наш внутренний класс - переопределенная панель. class GroupPanel extends JPanel { public Dimension getPreferredSize() { return new Dimension(250, 0); } }
Теперь мы сделаем так, чтобы наш интерфей "ожил". Сделаем реакции на кнопки,изменения групп и спинера.
Будем вызывать методы, в которых будут стоять "заглушки" - сообщения о том, что метод вызван. У нас
сообщения приходят от четырех видов компонентов - меню, кнопка, спинер, список.
Для меню и кнопок используется один вид листенера - ActionListener. Давайте теперь расставим все наши листенеры и сделаем "заглушки". Можно запустить наше приложение и проверить все наши управляющие компоненты - по идее все должно работать, т.е. выдавать сообщения на каждое действие - меню, кнопки, перемещение по списку групп, изменение величины в спинере. Комментарии к коду смотрите прямо в тексте программы. StudentsFrame.java package students.frame; import java.util.Vector; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.BorderLayout; import java.awt.Component; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; 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.JTable; import javax.swing.JSpinner; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; 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"; private ManagementSystem ms = null; private JList grpList; private JTable stdList; private JSpinner spYear; public StudentsFrame() throws Exception { // Устанавливаем 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)); // Получаем коннект к базе и создаем объект ManagementSystem ms = ManagementSystem.getInstance(); // Получаем список групп Vector<Group> gr = new Vector<Group>(ms.getGroups()); // Создаем надпись 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); } // Метод для обеспечения интерфейса 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"); } public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { try { // Мы сразу отменим продолжение работы, если не сможем получить // коннект к базе данных StudentsFrame sf = new StudentsFrame(); sf.setDefaultCloseOperation(EXIT_ON_CLOSE); sf.setVisible(true); } catch (Exception ex) { ex.printStackTrace(); } } }); } } // Наш внутренний класс - переопределенная панель. class GroupPanel extends JPanel { public Dimension getPreferredSize() { return new Dimension(250, 0); } } Модель для таблицыТеперь давайте реализуем первую команду - перегрузка списка студентов. Как уже упоминалось выше, нам предстоит определить свою модель. Реализация полной модели, т.е. интерфейса TableModel - задача сложная. В большинстве случаев нам необходимо только реализовать несколько методов. Понимая это разработчики Java создали класс AbstractTableModel, который реализует большинство необходимых методов. Для создания своей модели достаточно переопределить всего 3 метода:
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(), который загружает список студентов для выделенной группы и года. StudentTableModel.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.Component; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; 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.JTable; import javax.swing.JSpinner; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; 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; import students.logic.Student; 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"; private ManagementSystem ms = null; private JList grpList; private JTable stdList; private JSpinner spYear; public StudentsFrame() throws Exception { // Устанавливаем 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)); // Получаем коннект к базе и создаем объект ManagementSystem ms = ManagementSystem.getInstance(); // Получаем список групп Vector<Group> gr = new Vector<Group>(ms.getGroups()); // Создаем надпись 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); } // Метод для обеспечения интерфейса 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<Student> s = ms.getStudentsFromGroup(g, y); // И устанавливаем модель для таблицы с новыми данными stdList.setModel(new StudentTableModel(new Vector<Student>(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"); } public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { try { // Мы сразу отменим продолжение работы, если не сможем получить // коннект к базе данных StudentsFrame sf = new StudentsFrame(); sf.setDefaultCloseOperation(EXIT_ON_CLOSE); sf.setVisible(true); // Перегрузка списка нам нужна в этом треде // т.к. при создании формы списка студентов еще нет sf.reloadStudents(); } catch (Exception ex) { ex.printStackTrace(); } } }); } } // Наш внутренний класс - переопределенная панель. 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, то вы узнаете, что прорисовка компонентов
идет в отдельном потоке (треде - EDT, мы уже упоминали его) и, что самое неприятное, именно в этом
же треде вызываются методы листенеров. Т.е. мы исправили модель, но т.к. наш метод еще не завершился,
обновление экрана не произошло. И, как вы сами понимаете, зависание какого-либо метода внутри обработки
выглядит ужасно. Поэтому очень хорошим выходом из данной ситуации может служить многопотоковость.
О потоках вы можете прочитать в документации, либо посмотреть очень приличную статью в FAQ Vingrad
Многопоточное программирование.
private void reloadStudents() {
// Создаем анонимный класс для потока
Thread t = new Thread() {
// Переопределяем в нем метод run
public void run() {
if (stdList != null) {
// Получаем выделенную группу
Group g = (Group) grpList.getSelectedValue();
// Получаем число из спинера
int y = ((SpinnerNumberModel) spYear.getModel()).getNumber().intValue();
try {
// Получаем список студентов
Collection<Student> s = ms.getStudentsFromGroup(g, y);
// И устанавливаем модель для таблицы с новыми данными
stdList.setModel(new StudentTableModel(new Vector<Student>(s)));
} catch (SQLException e) {
JOptionPane.showMessageDialog(StudentsFrame.this, e.getMessage());
}
}
// Вводим искусственную задержку на 3 секунды
try {
Thread.sleep(3000);
} catch (Exception e) {
}
}
// Окончание нашего метода run
};
// Окончание определения анонимного класса
// И теперь мы запускаем наш поток
t.start();
}
Теперь даже несмотря на то, что наш метод ждет после обновления модели 3 секунды, само обновление экрана происходит сразу. Что несомненно приятно. Команды "очистить группу", "удалить студента" не требуют каких-либо сложных действий. Желательно просто спросить у пользователя подтверждения и все. Приведем код для методов deleteStudent() и clearGroup()
// метод для удаления студента
private void deleteStudent() {
Thread t = new Thread() {
public void run() {
if (stdList != null) {
StudentTableModel stm = (StudentTableModel) stdList.getModel();
// Проверяем - выделен ли хоть какой-нибудь студент
if (stdList.getSelectedRow() >= 0) {
if (JOptionPane.showConfirmDialog(StudentsFrame.this,
"Вы хотите удалить студента?", "Удаление студента",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
// Вот где нам пригодился метод getStudent(int rowIndex)
Student s = stm.getStudent(stdList.getSelectedRow());
try {
ms.deleteStudent(s);
reloadStudents();
} catch (SQLException e) {
JOptionPane.showMessageDialog(StudentsFrame.this, e.getMessage());
}
}
} // Если студент не выделен - сообщаем пользователю, что это необходимо
else {
JOptionPane.showMessageDialog(StudentsFrame.this, "Необходимо выделить студента в списке");
}
}
}
};
t.start();
}
// метод для очистки группы
private void clearGroup() {
Thread t = new Thread() {
public void run() {
// Проверяем - выделена ли группа
if (grpList.getSelectedValue() != null) {
if (JOptionPane.showConfirmDialog(StudentsFrame.this,
"Вы хотите удалить студентов из группы?", "Удаление студентов",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
// Получаем выделенную группу
Group g = (Group) grpList.getSelectedValue();
// Получаем число из спинера
int y = ((SpinnerNumberModel) spYear.getModel()).getNumber().intValue();
try {
// Удалили студентов из группы
ms.removeStudentsFromGroup(g, y);
// перегрузили список студентов
reloadStudents();
} catch (SQLException e) {
JOptionPane.showMessageDialog(StudentsFrame.this, e.getMessage());
}
}
}
}
};
t.start();
}
Для сборки нам потребуется команда
javac students/frame/*.java students/logic/*.java Для запуска нам надо указать в CLASSPATH файл mysqlJDBC-3.1.13-bin.jar java -cp .;mysql-connector-java-3.1.13-bin.jar students.frame.StudentsFrame Теперь мы подошли вплотную к завершающей стадии. Нам осталось только реализовать команды для добавления и редактирования студентов, переноса студентов из одной группы в другую и отчет по всем студентам. Это мы сделаем в следующей части нашего проекта - Часть 6 - GUI - заключительные классы Архив с исходными кодами: Исходный код Содержание |