Первый вариант пользовательского интерфейса
Если бы Вы попробовали собрать наш первый поект и запустить его под ОС Windows и при этом закомментировать строки перенаправления вывода данных, то скорее всего Вас постигло большое разочарование — на экране были сплошные закорючки и ничего не было понятно. Вероятнее всего Вы уже догадались, что проблема кроется в том, что текст мы набирали в кодировке Windows, а из командной строки весь вывод шел в кодировке MS-DOS.
Для того, чтобы Вам было понятнее, да и интереснее, давайте сделаем простой пользовательский интерфейс, заодно рассмотрим некоторые моменты создания такого рода приложений.
В данный момент мы не ставим своей целью получить что-то очень законченное — скорее это будет черновик, который мы потом сделаем более привлекательным и более сложным. Но мы сделаем приложение, которое пользуется некоторыми нашими командами — надо же на чем-то проверять наши разработки.
Предварительно мы опишем то, что хочется увидеть на экране.
- Список групп
- Список студентов для группы
- Элементы для редактирования — добавить, удалить, редактировать данные о студенте
- Элементы для работы с группой — удаление всех студентов и перевод студентов в другую группу
В этой части мы не будем реализовывать все команды, но кое-чем все-таки сможем воспользоваться. Давайте рассмотрим более подробно, какие же конкретно элементы управления нам потребуются. В первую очередь отметим, что использовать мы будем более новую библиотеку графических элементов SWING. Тем, кто захочет попробовать свои силы в более старом пакете AWT — придется это сделать самостоятельно. В некоторых случаях пакет AWT просто необходим — когда Вы пишете апплет для выполнения его в броузере, который поддерживает только старую версию JAVA 1.1.
Для показа групп и студентов нам потребуется список. Элементами для команд на редактирование пока будут кнопки. Для ввода информации — диалоговые окна. Ну и конечно же нам потребуется самое главное окно, в котором мы будем выполнять все наши действия.
Самое главное окно обычно наследуется от класса JFrame. Для того, чтобы создать очень простое оконное приложение надо очень мало строк.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import javax.swing.JFrame; import javax.swing.SwingUtilities; public class MainFrame extends JFrame { public MainFrame() { setBounds(100, 100, 300, 200); } public static void main(String[] args) { // Запуск формы лучше производить в специальном треде // event-dispatching thread - EDT SwingUtilities.invokeLater(new Runnable() { public void run() { MainFrame mf = new MainFrame(); mf.setDefaultCloseOperation(EXIT_ON_CLOSE); mf.setVisible(true); } }); } } |
Небольшой комментарий: В самом конструкторе нашего приложения (MainFrame) мы вызываем только один метод, который уже существует в JFrame — setBounds. Этот метод служит для установки размеров нашего окна.
Ну и конечно же метод main — первой строкой мы создаем объект, во второй строке мы устанавливаем поведение формы при закрытии (в данном случае мы говорим, чтобы при закрытии окна заканчивалось и приложение, а третья строка содержит метод, устанавливающий, что наша форма станет видимой. И все теперь готово. Приложение можно будет запускать.
ЗАМЕЧАНИЕ: Вы явно должны были обратить внимание на несколько необычный вызов в методе main. Такой вызов связан с тем, что все события внутри системы Swing происходят в отдельном треде — Event Dispatching Thread — EDT. Желательно (я бы сказал необходимо) ВСЕ операции с графикой делать в нем, т.к. некоторые операции не являются потоко-защищенными (т.е. если вы будете обращаться к форме из разных тредов, то результат может быть не очень красивый). Если вам пока сложно точно понять принцип такого запуска — просто пользуйтесь им. Позже разберетесь.
Для сборки и запуска нашего простого примера используются команды
javac MainFrame.java
java MainFrame
Конечно же мы рассмотрели очень простое приложение — нам необходимо будет его усложнить. Для этого мы рассмотрим несколько основных понятий, которые необходимо усвоить для более ясного представления, как работать с этой весьма разнообразной библиотекой.
Контейнеры
Для удобства работы Вам полезно будет представлять себе, что каждый элемент пользовательского интерфейса — это реальный объект, который можно положить на экран, который может принимать сообщения, у которого есть определенные свойства и т.д.
Но куда же мы должны класть наши элементы — ведь у нас сейчас перед глазами пустое окно (которое уже можно перемещать по экрану, менять размеры). Как Вы сам догадались — конечно же на окно. Точнее в ту область окна, которая находится под заголовком — так называемая «клиентская область». Если немного подумать, то становится ясно, что элементы можно класть на какие-то другие элементы. И именно те элементы, которые могут содержать в себе другие элементы называются контейнерами. Понятие контейнера очень рапространено — это может быть не только визуальный элемент (Но мы об этом поговорим в другой раз). Контейнером называют практически все, что может содержать и управлять другими элементами.
Чем важно понятие контейнера для визуальных компонентов ? Тем, что с их помощью можно группировать элементы. Вы может положить на один большой контейнер два поменьше и в каждый поместить какие-то элементы. Каждый из маленьких контейнеров может управлять размещением собственных элементов. Когда Вам надо будет разместить много элементов управления на форме — помните о контейнерах, думайте в их терминах. Если вы хотите определенным образом скомпоновать какие-либо элементы — знайте, что Вам потребуется контейнер.
Мы будем использовать самый простой и наверно самый распространенный контейнер — обычную панель — класс JPanel. Именно объект этого класса содержит «клиентская область формы». Доступ к ней осуществляется через вызов getContentPane(). Немного позже Вы увидите пример вызова.
Слушатели — Listeners
Конечно же практически каждый визуальный компонент не просто показывает себя на экране — он должен как-то реагировать на действия пользователя. И в большинстве случаев он это делает — кнопка нажимается, список прокручивается и т.д. Но ведь часто нам нужно не просто нажать кнопку — нам нужно, чтобы за этим нажатием выполнялось то, что нам хочется. Так каким образом можно узнать, что кнопку нажали, что над панелью провели мышкой ?
Для этого существует механизм слушателей — listeners.
Идея очень простая — тот, кто реагирует на какие-либо события содержит список объектов-слушателей, которые хотели бы узнавать о конкретном событии. Та же кнопка при своем нажатии, имея такой список, пробегает по нему и сообщает каждому объекту, который зарегистрировался как слушатель нажатия кнопки (можно зарегистрировать слушатель определенного события, произошедшего с кнопкой — движение мышки, нажатие кнопки мышки вниз и т.д.), что кнопку нажали. Т.к. объекты, которые хотят узнать о событии имеют разных предков (кроме конечно тех, которые произошли непосредственно от Object), то чтобы можно было им сообщать о событии единообразно все эти объекты должны реализовывать какой-то определенный интерфейс. Например для того, чтобы добавить слушателя для кнопки существует интерфейсActionListener (обратите внимание, что сам интерфейс имеет слово Listener — это обычное соглашение по именованию). Там всего один метод — actionPerformed. Также в подавляющем большинстве случаев в метод интерфейса передается параметр, который описывает параметры события. Например при событии «движение» от мышки хорошо передавать координаты мыши. При нажатии кнопки на клавиатуре — код клавиши. И т.д.
Модели
Уже достаточно давно в программирование используется такое понятие как паттерн (шаблон). Паттерны — это набор стандартных решений для стандартных задач. Если Вы хотите подробнее познакомиться с ними — отсылаю Вас к книге Э. Гамма «Приемы объектно-ориентированного проектирования — паттерны проектирования». Знание паттернов проектирования — это большой плюс. Не пренебрегайте ими.
Мы же рассмотрим здесь один такой паттерн — Model-View-Controller. Основная идея этого шаблона состоит в том, что поведение какого-либо сложного компонента системы разбивается на три части — модель, представление и контроллер.
Модель содержит данные, представление показывает, а контроллер реагирует на события. Чаще всего контроллер обрабатывает события и меняет модель, а потом сама модель может используя представление показывать свое содержимое (или контроллер может передать модель представлению, чтобы то модель показало).
Таким образом легко соединять и использовать разные представления для одной модели, а также использовать разные модели для одного представления. По сути это выглядит как если показывать текст в редакторе Notepad или в броузере — содержимое файла остается прежним. Касательно замены модели — в редакторе Word вы можете смотреть разные файлы, но визуально Word со своими компонентами вряд ли изменится сильно.
Мы увидим практическое применение этого несколько позже. А пока надо просто запомнить — если Вы хотите изменить состояние визуального компонента (добавить строки в список или удалить, изменить дерево) ищите у компонента метод getModel и меняйте данные модели. Практически каждый визуальный элемент SWING имеет модель, которая может быть изменена. И уже сама модель путем вызова определенных внутренних методов меняет визуальное представление компонента. Именно через нее Вы можете менять данные, которые отображает визуальный компонент. Не пытайтесь воздействовать непосредственно на элементы, которые Вы передаете в качестве параметров или менять что-либо прямо в визуальном компоненте. Например, элемент списка JList получает в качестве списка Vector. Так вот не пытайтесь менять переданный элемент типа Vector — последствия могут быть самые непредсказуемые. Для корректной работы Вам необходимо получить модель у визуального компонента и работать непосредственно с ней. Несколько позже мы увидим как это происходит — нам же придется менять содержимое списка студентов.
В процессе создания нашего примера мы увидим примеры использования всех трех понятий, которые мы рассмотрели. Правда не в этой части — здесь мы рассмотрим только некоторые.
Давайте для начала создадим достаточно простое приложение, которое не умеет многое. Я здесь преследую две цели:
- Не загружать пример. Если сразу дать новичку полный код приложения, то скорее всего он не будет в нем разбираться.
- Показать процесс конструирования формы — какие элементы можно использовать, как их располагать на форме, как пользоваться менеджерами разметки. Мы затронем понятие контейнера — сможете посмотреть все на практике.
Прежде чем смотреть код — немного слов о менеджере разметки — layout manager. Во-первых, достаточно подробно он рассмотрен здесь:
Что такое LayoutManager
Если же описать это понятие в двух словах, то можно сказать следующее: любой контейнер должен управлять тем, что в нем находится. В одном случае удобно располагать компоненты один под другим, в другом — рядом, в третьем — в виде таблицы. Именно для этой задачи каждый контейнер имеет у себя layout manager — объект, который занимается распределением объектов по контейнеру. Наше приложение сейчас будет уметь мало — мы вернемся к нему в других частях. А пока опишем, что мы собираемся создавать.
Форма будет разделена на две части — верхнюю и нижнюю. На верхней части будет компонент для ввода года. Мы его сделаем в виде числового слайдера (спинера). В общем элемент со стрелками вверх-вниз. Нижняя часть будет несколько сложнее — она поделится на две части. На левой будет список групп, а на правой список студентов. Пока у нас будет полный список. Когда мы будем более подробно рассматривать SWING мы усложним наше приложение.
Ниже приведены наши классы. Student.java и Group.java пока остаются без изменений. А вот в классе ManagementSystem мы удалили метод main. Теперь приведем непосредственно код уже всех наших частей:
Student.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
package students.logic; import java.text.Collator; import java.text.DateFormat; import java.util.Date; import java.util.Locale; 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; // get/set для ДАТА РОЖДЕНИЯ public Date getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } // get/set для ГОД ОБУЧЕНИЯ public int getEducationYear() { return educationYear; } public void setEducationYear(int educationYear) { this.educationYear = educationYear; } // get/set для ИД ГРУППЫ public int getGroupId() { return groupId; } public void setGroupId(int groupId) { this.groupId = groupId; } // get/set для ИД СТУДЕНТА public int getStudentId() { return studentId; } public void setStudentId(int studentId) { this.studentId = studentId; } // get/set для ИМЯ public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } // get/set для ОТЧЕСТВО public String getPatronymic() { return patronymic; } public void setPatronymic(String patronymic) { this.patronymic = patronymic; } // get/set для ФАМИЛИЯ public String getSurName() { return surName; } public void setSurName(String surName) { this.surName = surName; } // get/set для ПОЛ public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } // DateFormat - класс для преобразования даты // в строку в определеннном формате. // Подробнее смотрите документацию по этому методу public String toString() { return surName + " " + firstName + " " + patronymic + ", " + DateFormat.getDateInstance(DateFormat.SHORT).format(dateOfBirth) + ", Группа ИД=" + groupId + " Год:" + educationYear; } public int compareTo(Object obj) { Collator c = Collator.getInstance(new Locale("ru")); c.setStrength(Collator.PRIMARY); return c.compare(this.toString(), obj.toString()); } } |
Group.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
package students.logic; public class Group { // поле ИД ГРУППЫ private int groupId; // поле ИМЯ ГРУППЫ private String nameGroup; // поле КУРАТОР private String curator; // поле СПЕЦИАЛЬНОСТЬ private String speciality; // get/set для КУРАТОР public String getCurator() { return curator; } public void setCurator(String curator) { this.curator = curator; } // get/set для ИД ГРУППЫ public int getGroupId() { return groupId; } public void setGroupId(int groupId) { this.groupId = groupId; } // get/set для ИМЯ ГРУППЫ public String getNameGroup() { return nameGroup; } public void setNameGroup(String nameGroup) { this.nameGroup = nameGroup; } // get/set для СПЕЦИАЛЬНОСТЬ public String getSpeciality() { return speciality; } public void setSpeciality(String speciality) { this.speciality = speciality; } public String toString() { return nameGroup; } } |
ManagementSystem.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
package students.logic; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.List; import java.util.TreeSet; public class ManagementSystem { private List<Group> groups; private Collection<Student> students; // Для шаблона Singletone статическая переменная private static ManagementSystem instance; // закрытый конструктор private ManagementSystem() { loadGroups(); loadStudents(); } // метод getInstance - проверяtт, инициализирована ли статическая // переменная (в случае надобности делает это) и возвращает ее public static synchronized ManagementSystem getInstance() { if (instance == null) { instance = new ManagementSystem(); } return instance; } // Метод создает две группы и помещает их в коллекцию для групп public void loadGroups() { // Проверяем - может быть наш список еще не создан вообще if (groups == null) { groups = new ArrayList<Group>(); } 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<Student>(); } 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<Group> getGroups() { return groups; } // Получить список всех студентов public Collection<Student> getAllStudents() { return students; } // Получить список студентов для определенной группы public Collection<Student> getStudentsFromGroup(Group group, int year) { Collection<Student> l = new TreeSet<Student>(); for (Student si : students) { if (si.getGroupId() == group.getGroupId() && si.getEducationYear() == year) { l.add(si); } } return l; } // Перевести студентов из одной группы с одним годом обучения в другую группу с другим годом обучения public void moveStudentsToGroup(Group oldGroup, int oldYear, Group newGroup, int newYear) { for (Student si : students) { if (si.getGroupId() == oldGroup.getGroupId() && si.getEducationYear() == oldYear) { si.setGroupId(newGroup.getGroupId()); si.setEducationYear(newYear); } } } // Удалить всех студентов из определенной группы public void removeStudentsFromGroup(Group group, int year) { // Мы создадим новый список студентов БЕЗ тех, кого мы хотим удалить. // Возможно не самый интересный вариант. Можно было бы продемонстрировать // более элегантный метод, но он требует погрузиться в коллекции более глубоко // Здесь мы не ставим себе такую цель Collection<Student> tmp = new TreeSet<Student>(); for (Student si : students) { if (si.getGroupId() != group.getGroupId() || si.getEducationYear() != year) { tmp.add(si); } } students = tmp; } // Добавить студента public void insertStudent(Student student) { // Просто добавляем объект в коллекцию students.add(student); } // Обновить данные о студенте public void updateStudent(Student student) { // Надо найти нужного студента (по его ИД) и заменить поля Student updStudent = null; for (Student si : students) { if (si.getStudentId() == student.getStudentId()) { // Вот этот студент - запоминаем его и прекращаем цикл updStudent = si; break; } } updStudent.setFirstName(student.getFirstName()); updStudent.setPatronymic(student.getPatronymic()); updStudent.setSurName(student.getSurName()); updStudent.setSex(student.getSex()); updStudent.setDateOfBirth(student.getDateOfBirth()); updStudent.setGroupId(student.getGroupId()); updStudent.setEducationYear(student.getEducationYear()); } // Удалить студента public void deleteStudent(Student student) { // Надо найти нужного студента (по его ИД) и удалить Student delStudent = null; for (Student si : students) { if (si.getStudentId() == student.getStudentId()) { // Вот этот студент - запоминаем его и прекращаем цикл delStudent = si; break; } } students.remove(delStudent); } } |
И наконец класс для нашей формы — StudentsFrame.java — здесь обязательно обратите внимание на комментарии в конструкторе. Именно здесь мы создаем все наши элементы в соответсвии с тем описанием, что приведен выше.
StudentsFrame.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
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.SwingUtilities; import javax.swing.border.BevelBorder; import students.logic.Group; import students.logic.ManagementSystem; import students.logic.Student; 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<Group> gr = new Vector<Group>(ms.getGroups()); // Создаем надпись left.add(new JLabel("Группы:"), BorderLayout.NORTH); // Создаем визуальный список и вставляем его в скроллируемую // панель, которую в свою очередь уже кладем на панель left grpList = new JList(gr); left.add(new JScrollPane(grpList), BorderLayout.CENTER); // Создаем правую панель для вывода списка студентов JPanel right = new JPanel(); // Задаем layout и задаем "бордюр" вокруг панели right.setLayout(new BorderLayout()); right.setBorder(new BevelBorder(BevelBorder.RAISED)); // Получаем список студентов Vector<Student> st = new Vector<Student>(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[]) { // Запуск формы лучше производить в специальном треде // event-dispatching thread - EDT SwingUtilities.invokeLater(new Runnable() { public void run() { StudentsFrame sf = new StudentsFrame(); sf.setDefaultCloseOperation(EXIT_ON_CLOSE); sf.setVisible(true); } }); } } |
ВНИМАНИЕ!!!
Возможно, Вы обратили внимание, что класс StudentFrame.java находится в другом пакете — students.frame
Теперь структура нашего каталога выглядит так:
1 2 3 4 5 6 7 |
<strong>/student - /frame - StudentsFrame.java - /logic - Student.java - Group.java - ManagementSystem.java</strong> |
Для сборки нам потребуется команда
javac students/frame/*.java students/logic/*.java
Для сборки примера из архива
javac -encoding UTF-8 students/frame/*.java students/logic/*.java
Для запуска наберите следующую команду:
java -cp . students.frame.StudentsFrame
Как видите наше приложение уже что-то может. Если Вы попробуете уменьшить окно, то в какой-то момент увидите, что появяться линейки прокрутки. Конечно, наши данные еще далеки от совершенства и каждый раз будут исчезать из памяти. Для того, чтобы они не исчезали мы используем Часть 3 — Базы данных