Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Часть 5 - Улучшаем интерфейс и вводим команды 
:(
    Опции темы
AntonSaburov
Дата 19.10.2006, 18:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Штурман
****


Профиль
Группа: Модератор
Сообщений: 5658
Регистрация: 2.7.2002
Где: Санкт-Петербург

Репутация: 51
Всего: 118



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

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

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

--------------------
PM MAIL WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0808 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.