Книга 1 - Начальные сведения
Книга 2 - Более профессиональный подход |
Студенческий отдел кадров Окончание работы с GUIДля окончательного вида нам необходимо сделать еще три вещи:
Самое главное находится в его конструкторе - тут мы создаем все необходимые элементы - выпадающий список для групп и спинер для года. Также обратите внимание на то, как мы обрабатываем нажатие кнопок - ловим событие листенером и устанавливаем внутренне поле result в true или false. Если объявить result как int, то по идее можно устанавливать фактически любое количество возможных результатов - т.е. любая кнопка может закрыть окно и можно потом проверить, какая конкретно была нажата. Кроме этого мы воспользовались достаточно сложным layout'ом - GridBagLayout. Вы можете почитать о нем в статье Что такое LayoutManager GroupDialog.java package students.frame; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; import java.util.Vector; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import students.logic.Group; public class GroupDialog extends JDialog implements ActionListener { private static final int D_HEIGHT = 150; // высота private final static int D_WIDTH = 200; // ширина private JSpinner spYear; private JComboBox groupList; private JButton btnOk = new JButton("OK"); private JButton btnCancel = new JButton("Cancel"); private boolean result = false; public GroupDialog(int year, List groups) { // Установить заголовок setTitle("Перенос группы"); // Создаем сложный layout для нашего окна GridBagLayout gbl = new GridBagLayout(); setLayout(gbl); // Создаем переменную для установки правил размещения GridBagConstraints c = new GridBagConstraints(); // Сразу задаем отступ от границ для каждого элемента c.insets = new Insets(5, 5, 5, 5); // Первый элемент - заголовок для поля выбора групп JLabel l = new JLabel("Новая группа:"); // После него можно будет еще помещать компоненты c.gridwidth = GridBagConstraints.RELATIVE; // Не заполняем все пространство, отведенное компоненту c.fill = GridBagConstraints.NONE; // "Привязываем" компонент к правому краю c.anchor = GridBagConstraints.EAST; // Устанавливаем это правило для нашего компонета gbl.setConstraints(l, c); // Добавляем компонент getContentPane().add(l); // Второй элемент - список групп groupList = new JComboBox(new Vector(groups)); // Элемент занимает всю оставшуюся ширину c.gridwidth = GridBagConstraints.REMAINDER; // Растягиваем компонент по всему пространству для него c.fill = GridBagConstraints.BOTH; // "Привязываем" его к левой части c.anchor = GridBagConstraints.WEST; // Устанавливаем это правило для нашего компонета gbl.setConstraints(groupList, c); // Добавляем компонент getContentPane().add(groupList); // Третий элемент - заголовок для поля выбора года l = new JLabel("Новый год:"); // После него можно будет еще помещать компоненты c.gridwidth = GridBagConstraints.RELATIVE; // Не заполняем все пространство, отведенное компоненту c.fill = GridBagConstraints.NONE; // "Привязываем" компонент к правому краю c.anchor = GridBagConstraints.EAST; // Устанавливаем это правило для нашего компонета gbl.setConstraints(l, c); // Добавляем компонент getContentPane().add(l); // Сразу увеличиваем группу на один год - для перевода spYear = new JSpinner(new SpinnerNumberModel(year + 1, 1900, 2100, 1)); // Элемент занимает всю оставшуюся ширину c.gridwidth = GridBagConstraints.REMAINDER; // Растягиваем компонент по всему пространству для него c.fill = GridBagConstraints.BOTH; // "Привязываем" его к левой части c.anchor = GridBagConstraints.WEST; // Устанавливаем это правило для нашего компонета gbl.setConstraints(spYear, c); // Добавляем компонент getContentPane().add(spYear); c.gridwidth = GridBagConstraints.RELATIVE; c.fill = GridBagConstraints.BOTH; btnOk.setName("OK"); // Добавляем листенер для кнопки btnOk.addActionListener(this); // Устанавливаем это правило для нашего компонета gbl.setConstraints(btnOk, c); // Добавляем компонент getContentPane().add(btnOk); btnCancel.setName("Cancel"); // Добавляем листенер для кнопки btnCancel.addActionListener(this); // Устанавливаем это правило для нашего компонета gbl.setConstraints(btnCancel, c); // Добавляем компонент getContentPane().add(btnCancel); // Устанавливаем поведение формы при закрытии - не закрывать совсем. setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); // Получаем размеры экрана Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); // А теперь просто помещаем его по центру, вычисляя координаты на основе полученной информации setBounds(((int) d.getWidth() - GroupDialog.D_WIDTH) / 2, ((int) d.getHeight() - GroupDialog.D_HEIGHT) / 2, GroupDialog.D_WIDTH, GroupDialog.D_HEIGHT); } // Возврат года, который установлен на форме public int getYear() { return ((SpinnerNumberModel) spYear.getModel()).getNumber().intValue(); } // Возврат группы, которая установлена на форме public Group getGroup() { if (groupList.getModel().getSize() > 0) { return (Group) groupList.getSelectedItem(); } return null; } // Получить результат, true - кнопка ОК, false - кнопка Cancel public boolean getResult() { return result; } // Обработка нжатия кнопок public void actionPerformed(ActionEvent e) { JButton src = (JButton) e.getSource(); if (src.getName().equals("OK")) { result = true; } if (src.getName().equals("Cancel")) { result = false; } setVisible(false); } } Пока не будем приводить полностью код StudentsFrame. Покажем только наш новый вариант метода moveGroup
// метод для переноса группы
private void moveGroup() {
Thread t = new Thread() {
public void run() {
// Если группа не выделена - выходим. Хотя это крайне маловероятно
if (grpList.getSelectedValue() == null) {
return;
}
try {
// Получаем выделенную группу
Group g = (Group) grpList.getSelectedValue();
// Получаем число из спинера
int y = ((SpinnerNumberModel) spYear.getModel()).getNumber().intValue();
// Создаем наш диалог
GroupDialog gd = new GroupDialog(y, ms.getGroups());
// Задаем ему режим модальности - нельзя ничего кроме него выделить
gd.setModal(true);
// Показываем диалог
gd.setVisible(true);
// Если нажали кнопку OK - перемещаем в новую группу с новым годом
// и перегружаем список студентов
if (gd.getResult()) {
ms.moveStudentsToGroup(g, y, gd.getGroup(), gd.getYear());
reloadStudents();
}
} catch (SQLException e) {
JOptionPane.showMessageDialog(StudentsFrame.this, e.getMessage());
}
}
};
t.start();
}
Теперь посмотрим, каким образом нам сделать класс для редактирования данных о студенте. Этот класс может служить как для добавления так и для редактирования - поля будут одинаковые. Отметим, что ИД студента должно где-то храниться и если этот ИД будет больше 0 - это может служить нам знаком, что студента надо редактировать - т.е. выполнить обновление данных. Если же ИД равен 0, то это значит, что данные надо будет добавить. Также у нас есть два варианта передачи данных:
Также необходимо отметить, что нам потребуется конструктор для класса Student - сейчас он конструируется прямо из ResultSet. Нам же необходимо сделать конструктор без параметров. Он в общем-то совсем ничего не делает, но правила языка требуют. Мы ранее упоминали, что при добавлении студента программа будет позволять добавлять студента без закрытия окна - это удобно, когда набирают данные о новой группе. В этом случае мы оставляем данные о группе и годе такими же, а остальные поля очищаем. Всего у нас будет 7 полей:
StudentDialog.java package students.frame; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Calendar; import java.util.Date; import java.util.Enumeration; import java.util.List; import java.util.Vector; import javax.swing.AbstractButton; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JRadioButton; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SpinnerDateModel; import javax.swing.SpinnerNumberModel; import students.logic.Group; import students.logic.ManagementSystem; import students.logic.Student; public class StudentDialog extends JDialog implements ActionListener { private static final int D_HEIGHT = 200; // высота окна private final static int D_WIDTH = 450; // ширина окна private final static int L_X = 10; // левая граница метки для поля private final static int L_W = 100; // ширина метки для поля private final static int C_W = 150; // ширина поля // Владелец нашего окна - вводим для вызова нужного нам метода private StudentsFrame owner; // Результат нажатия кнопок private boolean result = false; // Параметры студента private int studentId = 0; private JTextField firstName = new JTextField(); private JTextField surName = new JTextField(); private JTextField patronymic = new JTextField(); private JSpinner dateOfBirth = new JSpinner(new SpinnerDateModel(new Date(), null, null, Calendar.DAY_OF_MONTH)); private ButtonGroup sex = new ButtonGroup(); private JSpinner year = new JSpinner(new SpinnerNumberModel(2006, 1900, 2100, 1)); private JComboBox groupList; // Второй параметр содержит знак - добавление студента или исправление public StudentDialog(List<Group> groups, boolean newStudent, StudentsFrame owner) { // После вставки студента без закрытия окна нам потребуется перегрузка списка // А для этого нам надо иметь доступ к этому методу в главной форме this.owner = owner; // Установить заголовок setTitle("Редактирование данных студента"); getContentPane().setLayout(new FlowLayout()); groupList = new JComboBox(new Vector<Group>(groups)); JRadioButton m = new JRadioButton("Муж"); JRadioButton w = new JRadioButton("Жен"); // Сделаем имя такое же, как требуется в баще данных - М/Ж m.setActionCommand("М"); w.setActionCommand("Ж"); // Добавим радио-кнопки в группу sex.add(m); sex.add(w); // Не будем использовать layout совсем getContentPane().setLayout(null); // Разместим компоненты по абсолютным координатам // Фамилия JLabel l = new JLabel("Фамилия:", JLabel.RIGHT); l.setBounds(L_X, 10, L_W, 20); getContentPane().add(l); surName.setBounds(L_X + L_W + 10, 10, C_W, 20); getContentPane().add(surName); // Имя l = new JLabel("Имя:", JLabel.RIGHT); l.setBounds(L_X, 30, L_W, 20); getContentPane().add(l); firstName.setBounds(L_X + L_W + 10, 30, C_W, 20); getContentPane().add(firstName); // Отчество l = new JLabel("Отчество:", JLabel.RIGHT); l.setBounds(L_X, 50, L_W, 20); getContentPane().add(l); patronymic.setBounds(L_X + L_W + 10, 50, C_W, 20); getContentPane().add(patronymic); // Пол l = new JLabel("Пол:", JLabel.RIGHT); l.setBounds(L_X, 70, L_W, 20); getContentPane().add(l); m.setBounds(L_X + L_W + 10, 70, C_W / 2, 20); getContentPane().add(m); // Сделаем по умолчанию женщину - из уважения w.setBounds(L_X + L_W + 10 + C_W / 2, 70, C_W / 2, 20); w.setSelected(true); getContentPane().add(w); // Дата рождения l = new JLabel("Дата рождения:", JLabel.RIGHT); l.setBounds(L_X, 90, L_W, 20); getContentPane().add(l); dateOfBirth.setBounds(L_X + L_W + 10, 90, C_W, 20); getContentPane().add(dateOfBirth); // Группа l = new JLabel("Группа:", JLabel.RIGHT); l.setBounds(L_X, 115, L_W, 25); getContentPane().add(l); groupList.setBounds(L_X + L_W + 10, 115, C_W, 25); getContentPane().add(groupList); // Год обучения l = new JLabel("Год обучения:", JLabel.RIGHT); l.setBounds(L_X, 145, L_W, 20); getContentPane().add(l); year.setBounds(L_X + L_W + 10, 145, C_W, 20); getContentPane().add(year); JButton btnOk = new JButton("OK"); btnOk.setName("OK"); btnOk.addActionListener(this); btnOk.setBounds(L_X + L_W + C_W + 10 + 50, 10, 100, 25); getContentPane().add(btnOk); JButton btnCancel = new JButton("Cancel"); btnCancel.setName("Cancel"); btnCancel.addActionListener(this); btnCancel.setBounds(L_X + L_W + C_W + 10 + 50, 40, 100, 25); getContentPane().add(btnCancel); if (newStudent) { JButton btnNew = new JButton("New"); btnNew.setName("New"); btnNew.addActionListener(this); btnNew.setBounds(L_X + L_W + C_W + 10 + 50, 70, 100, 25); getContentPane().add(btnNew); } // Устанавливаем поведение формы при закрытии - не закрывать совсем. setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); // Получаем размеры экрана Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); // А теперь просто помещаем его по центру, вычисляя координаты на основе полученной информации setBounds(((int) d.getWidth() - StudentDialog.D_WIDTH) / 2, ((int) d.getHeight() - StudentDialog.D_HEIGHT) / 2, StudentDialog.D_WIDTH, StudentDialog.D_HEIGHT); } // Установить поля соответственно переданным данным о студенте public void setStudent(Student st) { studentId = st.getStudentId(); firstName.setText(st.getFirstName()); surName.setText(st.getSurName()); patronymic.setText(st.getPatronymic()); dateOfBirth.getModel().setValue(st.getDateOfBirth()); for (Enumeration e = sex.getElements(); e.hasMoreElements();) { AbstractButton ab = (AbstractButton) e.nextElement(); ab.setSelected(ab.getActionCommand().equals(new String("" + st.getSex()))); } year.getModel().setValue(new Integer(st.getEducationYear())); for (int i = 0; i < groupList.getModel().getSize(); i++) { Group g = (Group) groupList.getModel().getElementAt(i); if (g.getGroupId() == st.getGroupId()) { groupList.setSelectedIndex(i); break; } } } // Вернуть данные в виде нового студента с соотвтествующими полями public Student getStudent() { Student st = new Student(); st.setStudentId(studentId); st.setFirstName(firstName.getText()); st.setSurName(surName.getText()); st.setPatronymic(patronymic.getText()); Date d = ((SpinnerDateModel) dateOfBirth.getModel()).getDate(); st.setDateOfBirth(d); for (Enumeration e = sex.getElements(); e.hasMoreElements();) { AbstractButton ab = (AbstractButton) e.nextElement(); if (ab.isSelected()) { st.setSex(ab.getActionCommand().charAt(0)); } } int y = ((SpinnerNumberModel) year.getModel()).getNumber().intValue(); st.setEducationYear(y); st.setGroupId(((Group) groupList.getSelectedItem()).getGroupId()); return st; } // Получить результат, true - кнопка ОК, false - кнопка Cancel public boolean getResult() { return result; } public void actionPerformed(ActionEvent e) { JButton src = (JButton) e.getSource(); // Добавляем студента, но не закрываем окно // Здесь мы не будем вызывать в отдельном потоке сохранение. // Оно не занимаем много времени и лишние действия здесь не оправданы if (src.getName().equals("New")) { result = true; try { ManagementSystem.getInstance().insertStudent(getStudent()); owner.reloadStudents(); firstName.setText(""); surName.setText(""); patronymic.setText(""); } catch (Exception sql_e) { JOptionPane.showMessageDialog(this, sql_e.getMessage()); } return; } if (src.getName().equals("OK")) { result = true; } if (src.getName().equals("Cancel")) { result = false; } setVisible(false); } } Теперь нам осталось сделать только форму для показа полного списка студентов. И этот момент мне бы хотелось оставить вам для самостоятельного изучения. Если очень захочется узнать, как это надо сделать - пишите свои отзывы. И если их будет много - я что-нибудь придумаю. Для сборки всех файлов нам потребуется команда
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 Итак, мы в общих чертах прошли по возможностям Java в части работы с коллекциями, базами данных и пользовательским интерфейсом. Теперь нас ждет путешествие в мир Web - мы посмотрим, как можно создавать Интернет-приложения, как пользоваться сервлетами, java-server pages (JSP) и другими технологиями Java для мира Интернет. Представляем - Часть 7 - Первые шаги в Интернет Архив с исходными кодами: Исходный код |