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

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

Error. Page cannot be displayed. Please contact your service provider for more details. (12)


Часть 2 - Введение в GUI

Поиск:
Первый вариант пользовательского интерфейса

Если Вы уже попробовали собрать наш первый поект и запустить его под ОС Windows, то скорее всего Вас постигло большое разочарование - на экране были сплошные закорючки и ничего не было понятно. Вероятнее всего Вы уже догадались, что проблема кроется в том, что текст мы набирали в кодировке Windows, а из комнадной строки весь вывод шел в кодировке MS-DOS.
Для того, чтобы Вам было понятнее, да и интереснее, давайте сделаем простой пользователский интерфейс, заодно рассмотрим некоторые моменты создания такого рода приложений.
В данный момент мы не ставим своей целью получить что-то очень законченное - скорее это будет черновик, который мы потом сделаем более привлекательным и более сложным. Но мы сделаем приложение, которое пользуется некоторыми нашими командами - надо же на чем-то проверять наши разработки.

Предварительно мы опишем то, что хочется увидеть на экране.
1. Список групп
2. Список студентов для группы
3. Элементы для редактирования - добавить, удалить, редактировать данные о студенте.
4. Элементы для работы с группой - удаление всех студентов и перевод студентов в другую группу.

В этой части мы не будем реализовывать все команды, но кое-чем все-таки сможем воспользоваться.

Давайте рассмотрим более подробно, какие же конкретно элементы управления нам потребуются.
В первую очередь отметим, что использовать мы будем более новую библиотеку графических элементов SWING. Тем, кто захочет попробовать свои силы в более старом пакете AWT - придется это сделать самостоятельно. В некоторых случаях пакет AWT просто необходим - когда Вы пишете апплет для выполнения его в броузере, который поддерживает только старую версию JAVA 1.1.
Для списка групп и студентов нам потребуется список. Элементами для команд на редактирование пока кнопки. И для ввода информации - диалоговые окна. Ну и конечно же нам потребуется самое главное окно, в котором мы будем выполнять все наши действия.

Самое главное окно обычно наследуется от класса JFrame. Для того, чтобы создать очень простое оконное приложение надо очень мало строк.

Код

package students.frame;

import javax.swing.JFrame;

public class MainFrame extends JFrame
{
  public MainFrame()
  {
    setBounds(100, 100, 300, 200);
  }

  public static void main(String[] args)
  {
    MainFrame mf = new MainFrame("Студенты");
    mf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    mf.setVisible(true);
  }
}

Небольшой комментарий: В самом конструкторе нашего приложения (MainFrame) мы вызываем только один метод, который уже существует в JFrame - setBounds. Этот метод служит для установки размеров нашего окна.
Ну и конечно же методв main - первой строкой мы создаем объект, во второй строке мы устанавливаем поведение формы при закрытии (в данном случае мы говорим, чтобы при закрытии окна заканчивалось и приложение, а третья строка содержит метод, устанавливающий, что наша форма станет видимой. И все теперь готово. Приложение можно будет запускать.
Код

javac MainFrame.java

java MainFrame


Конечно же мы рассмотрели очень просто приложение - нам необходимо будет его усложнить. Для этого мы рассмотрим несколько основных понятий, которые необходимо усвоить для более ясного представления, как работать с этой весьма разнообразной библиотекой.

Контейнеры
Для удобства работы Вам полезно будет представлять себе, что каждый элемент пользовательского интерфейса - это реальный объект, который можно положить на экран, который может принимать сообщения, у которого есть определенные свойства и т.д.
Но куда же мы должны класть наши элементы - ведь у нас сейчас перед глазами пустое окно (которое уже можно перемещать по экрану, менять размеры). Как Вы сам догадались - конечно же на окно. Точнее в ту область окна, которая находится под заголовком - так называемая "клиентская область". Если немного подумать, что становиться ясно, что элементы можно класть на какие-то другие элементы. И именно те элементы, которые могут содержать в себе другие элементы называются контейнерами. Понятие контейнера очнь рапространено - это может быть не только визуальный элемент. Контейнером называют практически все, что может содержать и управлять другими элементами.
Чем важно понятие контейнера для визуальных компонентов? Тем, что с их помощью можно группировать элементы. Вы может положить на один большой контейнер два поменьше и в каждый поместить какие-то элементы. Каждый из маленьких контейнеров моэет управлять размещением собственных элементов.
Кода Вам надо будет разместить много элементов управления на форме - помните о контейнерах, думайте в их терминах. Если вы хотите определенным образом скомпоновать какие-либо элементы - знайте, что Вам потребуется контейнер.
Мы будем использовать самый простой и наверно самый распространенный контейнер - обычную панель. Класс JPanel.
Именно объект этого класса содержит "клиентская область формы". Доступ к ней осуществляется через вызов getContentPane(). Немного позже Вы увидите пример вызова.

Слушатели - Listeners
Конечно же практически каждый визуальный компонент не просто показывает себя на экране - он дожен как-то реагировать на действия пользователя. И в большинстве случаев он это делает - кнопка нажимается, список прокручивается и т.д. Но ведь часто нам нужно не просто нажать кнопку - нам нужно, чтобы за этим нажатием выполнялось то, что нам хочется. Так каким образом можно узнать, что кнопку нажали, что над панелью провели мышкой?
Для этого существует механизм слушателей - listeners.
Идея очень простая - тот, кто реагирует на какие-либо события содержит список объектов-слушателей, которые хотели бы узнавать о конкретном событии. Та же кнопка при своем нажатии имея такой список пробегает по нему и сообщает каждому объекту, который зарегистрировался, что кнопку нажали.
Т.к. объекты, которые хотят узнать о событии имеют разных предков (кроме конечно тех, которые произошли непосредственно от Object), то чтобы можно было им сообщать о событии единообразно все эти объекты должны реализовывать какой-то определенный интерфейс. Например для того, чтобы добавить слушателя для кнопки существует интерфейс ActionListener (обратите внимание, что сам интерфейс имеет слово Listener). Там всего один метод - actionPerformed. Также в подавляющем большинстве случаев в метод интерфейса передается параметр, который описывает параметры события. Например при событии "движение" от мышки хорошо передавать координаты мыши. При нажатии кнопки на клавиатуре - код клавиши. И т.д.

Модели
Уже достаточно давно в программирование используется такое понятие как паттерн (шаблон). Паттерны - это набор стандартных решений для стандартных задач.
Если Вы хотите подробнее познакомиться с ними - отсылаю Вас к книге Э. Гамма "Приемы объектно-ориентированного проектирования - паттерны проектирования".
Знание паттернов проектирования - это большой плюс. Не пренебрегайте ими.

Мы же рассмотрим здесь один такой паттерн - Model-View-Controller.
Основная идея этого шаблона состоит в том, что поведение какого-либо сложного компонента системы разбивается на три части - модель, представление и контроллер.
Модель содержит данные, представление показывает, а контроллер реагирует на события. Чаще всего контролеер передает события модели изменяя ее, а уже сама модель может используя представление показывать свое содержимое.
Таким образом легко соединять и использовать разные представления для одной модели, а также использовать разные модели для одного представления.
По сути это выглядит как если показывать текст в редакторе Notepad или в броузере - содержимое файла остается прежним.
Касательно замены модели - в редакторе Word вы можете смотреть разные файлы, но визуально Word со своими компонентами вряд ли изменится сильно.
Мы увидим практическое примененеи этого несколько позже. А пока надо просто запомнить - если Вы хотите изменить состояние визуального компонета (добавить строки в списко ли удалить, изменить дерево) ищите у компонента метод getModel и меняйте данные модели.
Практически каждый визуальный элемент SWING имеет модель, которая может быть изменена. И уже сама модель путем вызова определенных внутренних методов меняет визуальное представление компонента. Именно через нее Вы можете менять данные, котороые отображает визуальный компонент. Не пытайтесь воздействовать непосредственно на элементы, которые Вы передаете в качестве параметров или менять что-либо прямо в визуальном компоненте. Наример  элемент списка JList получает в качестве списка Vector. Так вот не пытайтесь менять переданный элемент типа Vector - последствия могут быть самые не предсказуемые. Для корректной работы Вам необходимо получить модель у визуального компонента и работать непосредственно с ней.
Несколько позже мы увидим как это происходит - нам же придется менять содержимое списка студентов.

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

Создадим простое приложение, которое не умеет многое. Я преследую две цели:
1. Не загружать пример. Если сразу дать новичку полный код приложения, то скорее всего он не будет в нем разбираться.
2. Показать процесс конструирования формы - какие элементы можно использовать, как их располагать на форме, как пользоваться менеджерами разметки. Мы затронем понятие контейнера - посмотрите на практике.

Прежде чем смотреть код - немного слов о менеджере разметки - layout manager.
Во-первых, достаточно подробно он рассмотрен здесь: http://vingrad.ru/JAVA-JAV-000105

А если в двух словах - любой контейнер должен управлять тем, что в нем находится. В одном случае удобно располагать компоненты один под другим, в другом - рядом, в третьем - в виде таблицы.
Именно для этой задачи каждый контейнер имеет у себя layout manager - объект, который занимается распределением объектов по контейнеру.
Наше приложение сейчас будет уметь мало - мы вернемся к нему в других частях. А пока опишем, что мы собираемся создавать.

Форма будет разделена на две части - верхнюю и нижнюю.
На верхней части будет компонент для ввода года. Мы его сделаем в виде числового слайдера (спинера). Вообщем элемент со стрелками вверх-вниз.
Нижняя часть будет несколько сложнее - она поделится на две части. На левой будет список групп, а на правой список студентов. Пока у нас будет полный список. Когда мы будем более подробно рассматривать SWING мы усложним наше приложение.

Ниже приведены наши классы. Student.java и Group.java пока остаются без изменений. А вот в классе ManagementSystem мы удалили метод main.

А теперь непосредственно код уже всех наших частей:

Student.java
Код

package students.logic;

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 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.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;

public class ManagementSystem
{
    private List groups;

    private Collection students;

    private static ManagementSystem instance;

    private ManagementSystem() {
      loadGroups();
      loadStudents();
    }

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

    public void loadGroups()
    {
      if (groups == null) {
        groups = new ArrayList();
      } else {
        groups.clear();
      }
      Group g = null;

      g = new Group();
      g.setGroupId(1);
      g.setNameGroup("Первая");
      g.setCurator("Доктор Борменталь");
      g.setSpeciality("Создание собачек из человеков");
      groups.add(g);

      g = new Group();
      g.setGroupId(2);
      g.setNameGroup("Вторая");
      g.setCurator("Профессор Преображенский");
      g.setSpeciality("Создание человеков из собачек");
      groups.add(g);
    }

    public void loadStudents()
    {
      if (students == null) {
        students = new TreeSet();
      } else {
        students.clear();
      }

      Student s = null;
      Calendar c = Calendar.getInstance();

      // Вторая группа
      s = new Student();
      s.setStudentId(1);
      s.setFirstName("Иван");
      s.setPatronymic("Сергеевич");
      s.setSurName("Степанов");
      s.setSex('М');
      c.set(1990, 3, 20);
      s.setDateOfBirth(c.getTime());
      s.setGroupId(2);
      s.setEducationYear(2006);
      students.add(s);

      s = new Student();
      s.setStudentId(2);
      s.setFirstName("Наталья");
      s.setPatronymic("Андреевна");
      s.setSurName("Чичикова");
      s.setSex('Ж');
      c.set(1990, 6, 10);
      s.setDateOfBirth(c.getTime());
      s.setGroupId(2);
      s.setEducationYear(2006);
      students.add(s);

      // Первая группа
      s = new Student();
      s.setStudentId(3);
      s.setFirstName("Петр");
      s.setPatronymic("Викторович");
      s.setSurName("Сушкин");
      s.setSex('М');
      c.set(1991, 3, 12);
      s.setDateOfBirth(c.getTime());
      s.setEducationYear(2006);
      s.setGroupId(1);
      students.add(s);

      s = new Student();
      s.setStudentId(4);
      s.setFirstName("Вероника");
      s.setPatronymic("Сергеевна");
      s.setSurName("Ковалева");
      s.setSex('Ж');
      c.set(1991, 7, 19);
      s.setDateOfBirth(c.getTime());
      s.setEducationYear(2006);
      s.setGroupId(1);
      students.add(s);
    }

    // Получить список групп
    public List getGroups()
    {
      return groups;
    }
    
    // Получить список всех студентов
    public Collection getAllStudents()
    {
      return students;
    }

    // Получить спсико студентов для определенной группы
    public Collection getStudentsFromGroup(Group group, int year) 
    {
      Collection l = new TreeSet();
  
      for(Iterator i=students.iterator(); i.hasNext();) {
        Student s = (Student)i.next();
        if(s.getGroupId()==group.getGroupId() && s.getEducationYear() == year) {
            l.add(s);
        }
      }
      return l;
    }

    // Перевести студентов из одной группы с одним годом обучения в другую группу с другим годом обучения
    public void moveStudentsToGroup(Group oldGroup, int oldYear, Group newGroup, int newYear)
    {
      for(Iterator i=students.iterator(); i.hasNext();) {
        Student s = (Student)i.next();
        if(s.getGroupId()==oldGroup.getGroupId() && s.getEducationYear()==oldYear) {
            s.setGroupId(newGroup.getGroupId());
            s.setEducationYear(newYear);
        }
      }
    }

    // Удалить всех студентов из определенной группы    
    public void removeStudentsFromGroup(Group group, int year)
    {
      List tmp = new ArrayList();
      for(Iterator i=students.iterator(); i.hasNext();) {
        Student s = (Student)i.next();
        if(s.getGroupId()!=group.getGroupId() || s.getEducationYear() != year) {
            tmp.add(s);
        }
      }
      students = tmp;
    }

    // Добавить студента
    public void insertStudent(Student student)
    {
      students.add(student);
    }
    
    // Обновить данные о студенте
    public void updateStudent(Student student)
    {
      Student delStudent = null;
      for(Iterator i=students.iterator(); i.hasNext();) {
        Student s = (Student)i.next();
        if(s.getStudentId() == student.getStudentId()) {
            delStudent = s;
            break;
        }
      }
      students.remove(delStudent);
      students.add(student);
    }
    
    // Удалить студента
    public void deleteStudent(Student student)
    {
      Student delStudent = null;
      for(Iterator i=students.iterator(); i.hasNext();) {
        Student s = (Student)i.next();
        if(s.getStudentId() == student.getStudentId()) {
            delStudent = s;
            break;
        }
      }
      students.remove(delStudent);
    }
}


StudentsFrame.java - здесь обязательно обратите внимание на комментарии в конструкторе. Именно здесь мы создаем все наши элементы в соответсвии с тем описанием, что приведен выше.
Код

package students.frame;

import java.util.Vector;

import java.awt.FlowLayout;
import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

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
{
  ManagementSystem ms = ManagementSystem.getInstance();
  private JList grpList;
  private JList stdList;
  private JSpinner spYear;

  public StudentsFrame()
  {
    // Устанавливаем layout для всей клиентской части формы
    getContentPane().setLayout(new BorderLayout());


    // Создаем верхнюю панел, где будет поле для ввода года
    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());

    // Создаем левую панель для вывода списка групп
    JPanel left = new JPanel();
    // Задаем layout и задаем "бордюр" вокруг панели
    left.setLayout(new BorderLayout());
    left.setBorder(new BevelBorder(BevelBorder.RAISED));

    // Получаем список групп
    Vector gr=new Vector(ms.getGroups());
    // Создаем надпись
    left.add(new JLabel("Группы:"), BorderLayout.NORTH);
    // Создаем визуальный список и вставляем его в скроллируемую
    // панель, которую в свою очередь уже кладем на панель left
    grpList = new JList(gr);
    left.add(new JScrollPane(grpList), BorderLayout.CENTER);

    // Создаем правую панель для вывода списка студентов
    JPanel right = new JPanel();
    // Задаем layout и задаем "бордюр" вокруг панели
    right.setLayout(new BorderLayout());
    right.setBorder(new BevelBorder(BevelBorder.RAISED));

    // Получаем список студентов
    Vector st=new Vector(ms.getAllStudents());
    // Создаем надпись
    right.add(new JLabel("Студенты:"), BorderLayout.NORTH);
    // Создаем визуальный список и вставляем его в скроллируемую
    // панель, которую в свою очередь уже кладем на панель right
    stdList = new JList(st);
    right.add(new JScrollPane(stdList), BorderLayout.CENTER);

    // Вставляем панели со списками групп и студентов в нижнюю панель 
    bot.add(left, BorderLayout.WEST);
    bot.add(right, BorderLayout.CENTER);

    // Вставляем верхнюю и нижнюю панели в форму
    getContentPane().add(top, BorderLayout.NORTH);
    getContentPane().add(bot, BorderLayout.CENTER);

    // Задаем границы формы   
    setBounds(100, 100, 600, 400);
  }


  public static void main(String args[])
  {
    StudentsFrame sf = new StudentsFrame();
    sf.setDefaultCloseOperation(EXIT_ON_CLOSE);
    sf.setVisible(true);
  }
}


ВНИМАНИЕ!!!
Возможно Вы обратили внимание, что класс StudentFrame.java находится в другом пакете - students.frame

Теперь структура нашего каталога выглядит так:
Код

- student
   - frame
      - StudentsFrame.java
   - logic
      - Student.java
      - Group.java
      - ManagementSystem.java

Для сборки нам потребуется команда
Код

javac students/frame/*.java students/logic/*.java

Для запуска
Код

java -cp . students.frame.StudentsFrame


Как видите наше приложение уже что-то может. Если Вы попробуете уменьшить окно, то в какой-то момент увидите, что появяться линейки прокрутки.

Конечно, наши данные еще далеки от совершенства и каждый раз будут исчезать из памяти. Для того, чтобы они не исчезали мы используем Часть 3 - Базы данных
Автор: AntonSaburov






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

 

 

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


Популярные:
  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. Проверить дубляжи в столбце


 

 

 
 
На главную