Полезное для программистов:

Фриланс
Новости
Статьи
   
Рубрики:


Часть 5 - Улучшаем интерфейс и вводим команды

Поиск:
Улучшаем интерфейс - листенеры, модель таблицы и потоки

Переделав наше приложения в плане работы с базой данных, вернемся к пользовательскому интерфейсу. Во-первых мы не реализовали достаточно много функций, во-вторых - наш интерфейс вообщем-то далек от совершенства.

Предварительно опишем (еще раз), что мы теперь хотим получить:
- В верхней части экрана у нас остается тот же самый спин. Но к нему надо приделать листенер, который будет вызывать обновление списка студентов.
- Список групп. Мы его оставим таким, как был. Только добавим листенер, который будет реагировать на изменение выделенной группы.
- Список студентов будет изменен. Во-первых будем показывать студентов из выделенной группы, а во-вторых будем показывать их не списком, а таблицей. Для таблицы мы создадим свю собственную модель. Особенности мы обсудим позже.
- Добавим три кнопки для редактирования студентов в нижнюю часть таблицы со списком студентов для редактирования
- Добавим две кнопки внизу списка групп - для удаления всех студентов из группы и для перевода всех студентов в другую группу. Хорошо наверно было бы сделать возможность выделять студентов и переводить только выделенных, но это мы оставим для самостоятельного изучения. Кроме этого мы сделаем наш список групп постоянным по ширине - иначе, если группы называются 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 "Многопоточное программирование" - http://vingrad.ru/JAVA-JAVA-000102
Если определить в двух словах, то поток это отдельный подпроцесс. Любое приложение имеет как минимум один поток, где все выполняется. Если Вы создаете еще один поток, то выполнение их происходит как бы параллельно - выполняется то один, то другой. Потоков можно создать очень много и каждый будет выполнять свою работу самостоятельно. Программа будет переключаться с выполнения одного потока на выполнение другого и будет создаваться впечатление, что потоки выполняются одновременно.

Реализуем наш метод в виде потока. Отметим еще одну особенность - JAVA позволяет Вам создавать "анонимные" классы. Т.е. Вы создаете класс прямо в коде, в нем же переопределяя нужные Вам методы. Посмотрите код нашего метода

Код

  // метод для обновления списка студентов для определенной группы
  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 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) {}
      }
      // Окончание нашего метода 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();
  }


Теперь мы подошли вплотную к завершающей стадии. Нам осталось только реализовать команды для добавления и редактирования студентов, переноса студентов из одной группы в другую и отчет по всем студентам. Это мы сделаем в следующей части нашего проекта.

Теперь еще раз приведем полный код всех наших классов, чтобы Вы могли их сразу скопировать, посмотреть и попробовать.

Student.java
Код

package students.logic;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;


public class Student implements Comparable
{
    private int studentId;
    private String firstName;
    private String surName;
    private String patronymic;
    private Date dateOfBirth;
    private char sex;
    private int groupId;
    private int educationYear;

    public Student(ResultSet rs) throws SQLException
    {
      setStudentId(rs.getInt("student_id"));
      setFirstName(rs.getString("firstName"));
      setPatronymic(rs.getString("surName"));
      setSurName(rs.getString("patronymic"));
      setSex(rs.getString("sex").charAt(0));
      setDateOfBirth(rs.getDate("dateOfBirth"));
      setGroupId(rs.getInt("group_id"));
      setEducationYear(rs.getInt("educationYear"));
    }

    public Date getDateOfBirth()
    {
      return dateOfBirth;
    }

    public void setDateOfBirth(Date dateOfBirth)
    {
      this.dateOfBirth = dateOfBirth;
    }

    public int getEducationYear()
    {
      return educationYear;
    }

    public void setEducationYear(int educationYear)
    {
      this.educationYear = educationYear;
    }

    public int getGroupId()
    {
      return groupId;
    }

    public void setGroupId(int groupId)
    {
      this.groupId = groupId;
    }

    public int getStudentId()
    {
      return studentId;
    }

    public void setStudentId(int studentId)
    {
      this.studentId = studentId;
    }

    public String getFirstName()
    {
      return firstName;
    }

    public void setFirstName(String firstName)
    {
      this.firstName = firstName;
    }

    public String getPatronymic()
    {
      return patronymic;
    }

    public void setPatronymic(String patronymic)
    {
      this.patronymic = patronymic;
    }

    public String getSurName()
    {
      return surName;
    }

    public void setSurName(String surName)
    {
      this.surName = surName;
    }

    public char getSex()
    {
      return sex;
    }

    public void setSex(char sex)
    {
      this.sex = sex;
    }

    public String toString()
    {
      return surName
        + " "
        + firstName
        + " "
        + patronymic
        + ", "
        + DateFormat.getDateInstance(DateFormat.SHORT).format(
          dateOfBirth) + ", Группа ИД=" + groupId+ " Год:"+educationYear;
    }

    public int compareTo(Object obj)
    {
      return this.toString().compareTo(obj.toString());
    }
}


Group.java
Код

package students.logic;

public class Group
{
    private int groupId;
    private String nameGroup;
    private String curator;
    private String speciality;

    public String getCurator()
    {
      return curator;
    }

    public void setCurator(String curator)
    {
      this.curator = curator;
    }

    public int getGroupId()
    {
      return groupId;
    }

    public void setGroupId(int groupId)
    {
      this.groupId = groupId;
    }

    public String getNameGroup()
    {
      return nameGroup;
    }

    public void setNameGroup(String nameGroup)
    {
      this.nameGroup = nameGroup;
    }

    public String getSpeciality()
    {
      return speciality;
    }

    public void setSpeciality(String speciality)
    {
      this.speciality = speciality;
    }

    public String toString()
    {
      return nameGroup;
    }
}


ManagementSystem.java
Код

package students.logic;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class ManagementSystem
{
  private static Connection con; 
  private static ManagementSystem instance;

  private ManagementSystem() {
    try {
      Class.forName("com.mysql.jdbc.Driver");
      String url = "jdbc:mysql://localhost/students";
      con = DriverManager.getConnection(url,"root", "");
    } catch (ClassNotFoundException e1) {
      e1.printStackTrace();
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }

  public static synchronized ManagementSystem getInstance()
  {
    if (instance == null) {
      instance = new ManagementSystem();
    }
    return instance;
  }

  public List getGroups() throws SQLException
  {
    List groups = new ArrayList();
    
    Statement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT group_id, groupName, curator, speciality FROM groups");
    while(rs.next()) {
      Group gr = new Group();
      gr.setGroupId(rs.getInt(1));
      gr.setNameGroup(rs.getString(2));
      gr.setCurator(rs.getString(3));
      gr.setSpeciality(rs.getString(4));

      groups.add(gr);
    }
    rs.close();
    stmt.close();
    
    return groups;
  }
    
  public Collection getAllStudents() throws SQLException
  {
    Collection students = new ArrayList();

    Statement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery(
        "SELECT student_id, firstName, patronymic, surName, "+
        "sex, dateOfBirth, group_id, educationYear FROM students ORDER BY surName, firstName, patronymic");
    while(rs.next()) {
      Student st = new Student(rs);
      students.add(st);
    }
    rs.close();
    stmt.close();

    return students;
  }

  public Collection getStudentsFromGroup(Group group, int year) throws SQLException 
  {
    Collection students = new ArrayList();

    PreparedStatement stmt = con.prepareStatement(
        "SELECT student_id, firstName, patronymic, surName, "+
        "sex, dateOfBirth, group_id, educationYear FROM students " +
        "WHERE group_id=? AND educationYear=? "+
        "ORDER BY surName, firstName, patronymic");
    stmt.setInt(1, group.getGroupId());
    stmt.setInt(2, year);
    ResultSet rs = stmt.executeQuery();
    while(rs.next()) {
      Student st = new Student(rs);
    
      students.add(st);
    }
    rs.close();
    stmt.close();
    
    return students;
  }

  public void moveStudentsToGroup(Group oldGroup, int oldYear, Group newGroup, int newYear) throws SQLException
  {
    PreparedStatement stmt = con.prepareStatement(
        "UPDATE students SET group_id=?, educationYear=? " +
        "WHERE group_id=? AND educationYear=?");
    stmt.setInt(1, newGroup.getGroupId());
    stmt.setInt(2, newYear);
    stmt.setInt(3, oldGroup.getGroupId());
    stmt.setInt(4, oldYear);
    stmt.execute();
  }
    
  public void removeStudentsFromGroup(Group group, int year) throws SQLException
  {
    PreparedStatement stmt = con.prepareStatement(
        "DELETE FROM students WHERE group_id=? AND educationYear=?");
    stmt.setInt(1, group.getGroupId());
    stmt.setInt(2, year);
    stmt.execute();
  }

  public void insertStudent(Student student) throws SQLException
  {
    PreparedStatement stmt = con.prepareStatement(
        "INSERT INTO students "+
        "(firstName, patronymic, surName, sex, dateOfBirth, group_id, educationYear) " +
        "VALUES (?, ?, ?, ?, ?, ?, ?)");
    stmt.setString(1, student.getFirstName());
    stmt.setString(2, student.getPatronymic());
    stmt.setString(3, student.getSurName());
    stmt.setString(4, new String(new char[] {student.getSex()} ));
    stmt.setDate(5, new Date(student.getDateOfBirth().getTime()));
    stmt.setInt(6, student.getGroupId());
    stmt.setInt(7, student.getEducationYear());

    stmt.execute();
  }
    
  public void updateStudent(Student student) throws SQLException
  {
    PreparedStatement stmt = con.prepareStatement(
        "UPDATE students SET "+
        "firstName=?, patronymic=?, surName=?, "+
        "sex=?, dateOfBirth=?, group_id=?, educationYear=? " +
        "WHERE student_id=?");
    stmt.setString(1, student.getFirstName());
    stmt.setString(2, student.getPatronymic());
    stmt.setString(3, student.getSurName());
    stmt.setString(4, new String(new char[] {student.getSex()} ));
    stmt.setDate(5, new Date(student.getDateOfBirth().getTime()));
    stmt.setInt(6, student.getGroupId());
    stmt.setInt(7, student.getEducationYear());
    stmt.setInt(8, student.getStudentId());
  
    stmt.execute();
  }
    
  public void deleteStudent(Student student) throws SQLException
  {
    PreparedStatement stmt = con.prepareStatement(
      "DELETE FROM students WHERE student_id=?");
    stmt.setInt(1, student.getStudentId());
    stmt.execute();
  }
}


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.java
Код

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;
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";

  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);
    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()
  {
    // Создаем анонимный класс для потока
    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 s = ms.getStudentsFromGroup(g, y);
            // И устанавливаем модель для таблицы с новыми данными
            stdList.setModel(new StudentTableModel(new Vector(s)));
          } catch (SQLException e) {
            JOptionPane.showMessageDialog(StudentsFrame.this, e.getMessage());
          }
        }
      }
      // Окончание нашего метода run
    };
    // Окончание определения анонимного класса
    // И теперь мы запускаем наш поток
    t.start();
  }

  // метод для переноса группы
  private void moveGroup()
  {
    JOptionPane.showMessageDialog(this, "moveGroup");
  }

  // метод для очистки группы
  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();
  }

  // метод для добавления студента
  private void insertStudent()
  {
    JOptionPane.showMessageDialog(this, "insertStudent");
  }

  // метод для редактирования студента
  private void updateStudent()
  {
    JOptionPane.showMessageDialog(this, "updateStudent");
  }

  // метод для удаления студента
  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 showAllStudents()
  {
    JOptionPane.showMessageDialog(this, "showAllStudents");
  }
}

// Наш внутренний класс - переопределенная панель.
class GroupPanel extends JPanel
{

  public Dimension getPreferredSize()
  {
    return new Dimension(250, 0);
  }
}


И теперь в следующей части мы полностью завершим наше приложение в части GUI интерфейса - встречайте Часть 6 - GUI - заключительные классы
Автор: AntonSaburov






Просмотров: 4268

 

 

Новые статьи:


Популярные:
  1. Как сделать цикличным проигрывание MIDI-файла?
  2. Создание AVI файла из рисунков
  3. Как устройство "отключить в данной конфигурации"?
  4. Kто в данный момент присоединен через Сеть?
  5. Как узнать количество доступной памяти?
  6. Как реализовать в RichEdit разноцветный текст?
  7. Как скрыть свое приложение от ProcessViewer
  8. Как программно нажать/скрыть/показ кнопку "Start"?
  9. Модуль работы с ресурсами в PE файлах
10. Функции вызова диалоговых окон выбора
11. Проверка граматики средствами Word'а из Delphi.
12. Модуль для упрощенного вызова сообщений
13. Функции для записи и чтение своих данных в, ЕХЕ- файле
14. Рекурсивный просмотр директорий
15. Network Traffic Monitor
16. Разные модули
17. Универсальная функция для обращения к любым экспортируем функциям DLL
18. Библиотека от VladS
19. Протектор для UPX'а
20. Еще об ICQ, сообщения по контакт листу?
21. Использование открытых интерфейсов
22. Теория и практика использования RTTI
23. Работа с TApplication
24. Примеры использования Drag and Drop для различных визуальных компонентов
25. Что такое порт? Правила для работы с портами
26. Симфония на клавиатуре
27. Загрузка DLL
28. Исправление автоинкремента
29. Взаимодействие с чужими окнами
30. Проверить дубляжи в столбце


 

 

 
 
На главную