Конспект лекций по Java. Занятие 20
( В.Фесюнов )
Продолжение знакомства со средствами Swing
Мы уже рассмотрели ряд визуальных компонент библиотеки Swing, такие как JLabel, JButton, JTextField, JTextArea, JPanel, JFrame, JScrollPane, JList . В частности мы рассмотрели JList , что дало нам возможность строить удобные в использовании приложения, в которых для навигации по какому-то массиву информации используется отображенный на экране список объектов.
Теперь мы можем строить довольно сложные и функционально развитые приложения. Однако, при усложнении приложений возникает простая, но весьма неприятная проблема — размер экрана ограничен и для всех компонент диалога, необходимых приложению, его может просто не хватить. Так, в примере с записной книжкой (см. задачу 18-го занятия) часть рабочей области экрана мы распределили под список. В случае такого простого объекта, как мы использовали для хранения информации о персоне, оставшейся части экрана достаточно для отображения информации. Но, если мы захотим расширить объем этой информации, то она просто не поместится на экран.
Одним из средств, помогающих решить такую проблему, является панель с закладками, которая в Swing реализована при помощи класса JTabbedPane .
Класс JTabbedPane
Мы уже сталкивались с классом JTabbedPane на 12-м занятии в примере Dlg3.java . Рассмотрим другой, более простой пример, чтобы познакомиться с этим классом подробнее.
Пример (файл Dlg6.java)
// Dlg6.java
// Визульное приложения с JTabbedPane.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Dlg6 extends JFrame {
Dlg6() {
super("Визульное приложения с JTabbedPane");
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e) {
}
setSize(400, 200);
Container c = getContentPane();
JTabbedPane tp = new JTabbedPane();
c.add(tp, BorderLayout.CENTER);
JPanel pn1 = new JPanel();
tp.add(pn1, "Hello");
JPanel pn2 = new JPanel();
tp.add(pn2, "Good-bye");
WindowListener wndCloser = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
};
addWindowListener(wndCloser);
setVisible(true);
}
public static void main(String[] args) {
Dlg6 d = new Dlg6();
}
}
Оттранслируем и запустим приложение. Приложение формирует такое окно

Здесь две закладки, между которыми можно переключаться. Рассмотрим, как это реализовано. Сначала создается объект JTabbedPane и заносится в центр экрана
JTabbedPane tp = new JTabbedPane(); c.add(tp, BorderLayout.CENTER);
Потом создаются две панели pn1 и pn2 и добавляются на панель с закладками.
JPanel pn1 = new JPanel(); tp.add(pn1, "Hello"); JPanel pn2 = new JPanel(); tp.add(pn2, "Good-bye");
При этом применяется метод add с двумя параметрами, второй из которых содержит текст. Именно этот текст выводится на данной закладке. Количество закладок определяется количеством панелей добавленных к экземпляру класса JTabbedPane . Т.е. сформировать панели с закладками довольно просто.
Класс JTabbedPane имеет два конструктора
public JTabbedPane()
Конструктор по умолчанию. Формирует горизонтальную линейку закладок вверху.
public JTabbedPane(int tabPlacement)
Позволяет разместить закладки как горизонтально, так и вертикально, внизу и вверху панели. Соответствующие константы: TOP, BOTTOM, LEFT, RIGHT .
Попробуем в нашем приложении заменить конструктор по умолчанию на конструктор с параметром JTabbedPane.LEFT , т.е.
JTabbedPane tp = new JTabbedPane(JTabbedPane.LEFT);
Оттранслируем и посмотрим результат.
Каждая из закладок — это панель, на которую можно поместить свои визуальные компоненты. Это позволяет разместить на одном и том же участке экрана достаточно большое количество информации.
Практическая работа
Воспользуемся этим для расширения возможностей нашего приложения с записной книжкой. Естественно, что фамилия и телефон, это очень мало и при формировании записной книжки может потребоваться другая информация.
Добавим на нашу записную книжку следующую информацию:
- "Фамилия"
- "Имя"
- "Отчество"
- "Отображать как" — изначально формируется как имя + фамилия, но потом можно изменить
- "Псевдоним" — может быть пустым
- "E-mail адрес(а)" — произвольное количество строк с e-mail адресами
- "Пол" — мужской(м), женский(ж), не указан(н)
- "Дата рождения" — может быть не указана
- "Телефон"
- "Факс"
- "Мобильный"
- "Дом, улица"
- "Город"
- "Штат/область"
- "ZIP/Почтовый индекс"
- "Страна"
- "Личная www-страница"
- "Заметки"
Начальный вариант класса Person для хранения всей этой информации выгладит так.
// Person.java
import java.io.*;
import java.util.Date;
public class Person implements Comparable,Serializable {
/**
* "Фамилия"
*/
private String lastName = null;
/**
* "Имя"
*/
private String firstName = null;
/**
* "Отчество"
*/
private String middleName = null;
/**
* "Отображать как" — изначально формируется как имя + фамилия,
* но потом можно изменить
*/
private String displayName = null;
/**
* "Псевдоним" — может быть пустым
*/
private String nickName = null;
/**
* "E-mail адрес(а)" — произвольное количество строк с e-mail адресами
*/
private String eMail = null;
/**
* "Пол" — мужской(м), женский(ж), не указан(н)
*/
private char gender = 'н';
/**
* "Дата рождения" — может быть не указана
*/
private Date birthday = null;
/**
* "Телефон"
*/
private String phone = null;
/**
* "Факс"
*/
private String facsimile = null;
/**
* "Мобильный"
*/
private String cellular = null;
/**
* "Дом, улица"
*/
private String streetAddr = null;
/**
* "Город"
*/
private String city = null;
/**
* "Штат/область"
*/
private String state = null;
/**
* "ZIP/Почтовый индекс"
*/
private String zip = null;
/**
* "Страна"
*/
private String country = null;
/**
* "Личная www-страница"
*/
private String homepage = null;
/**
* "Заметки"
*/
private String memo = null;
public Person() {
}
...
public int compareTo(Object another) {
return displayName.compareTo(((Person)another).displayName);
}
public String toString() {
return (displayName);
}
}
Как видим, результат (даже начальный) получился довольно внушительным. Следовательно, он требует к себе соответствующего внимания.
Сделаем два замечания.
- В классе Person для хранения даты использован класс java.util.Date . С его использованием мы познакомимся в процессе дальнейшей разработки нашего приложения.
- Классы, подобные Person , называют бизнес-классами (а экземпляры этих классов — бизнес-объектами ). Такие классы предназначены для собственно хранения бизнес-информации, тогда как все остальные классы нашего приложения предназначены для манипулирования этой информацией.
Для бизнес-классов существует сложившаяся практика их построения и использования. Некоторые детали мы можем "прочувствовать" на нашем приложении.
Так, в предыдущих версиях приложения мы для построения нашего бизнес-объекта просто использовали конструктор
public Person(String name, String phone)
Это было сделано для упрощения, но сейчас такой вариант конструктора не подходит. В нем пришлось бы перечислить в качестве параметров все поля нашего класса, а это не очень удобно.
Поэтому в новой версии нашего приложения мы поступим следующим образом.
1. В качестве единственного конструктора будем использовать конструктор по умолчанию.
2. Все поля класса проинициализируем значением null .
3. Для каждого поля напишем два метода доступа ( getter и setter ), которые позволят выбирать значение поля (метод get<имя_поля>()) и заносить информацию в поле (метод set<имя_поля>(...)).
4. В главном классе приложения введем поле curObject :
private Person curObject;
5. При выборе строки списка в curObject будем заносить ссылку на объект Person , соответствующий данной строке.
6. При вводе новой записи по кнопке "Новая запись" будем создавать пустой объект Person и ссылку на него заносить в curObject .
7. Для каждого поля на экране реализуем слушателя, который будет заносить значение этого поля в curObject при помощи соответствующего метода set<имя_поля>(...).
Приведем пример нескольких get- и set-методов для класса Person
public String getEMail() {
return eMail;
}
public void setEMail(String eMail) {
this.eMail = eMail;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
Следует обратить внимание, что имена методов сформированы в соответствии с именами полей, а типы возвращаемых значений в get-методах и типы параметров в set-методах соответствуют типам полей.
Теперь, когда мы разобрались с общими принципами работы с полями в нашем приложении, вернемся к проблеме размещения всей этой информации на экране.
Требуется реализовать приложение так, чтобы экран выглядел следующим образом
1-я закладка

2-я закладка

3-я закладка

По сравнению с предыдущей версией здесь кнопка "Новая запись" перенесена в левую панель и рядом добавлена кнопка "Удалить запись". Это позволило освободить пространство в правой панели. Кроме того, в данном варианте более естественным является диалог, в котором мы по кнопке "Новая запись" не переходим в режим создания новой записи (в предыдущей версии для этого служил флажок newMode ), а сразу добавляем пустую запись в конец списка, а потом ее редактируем. Поэтому кнопки добавления/удаления записей более естественно поместить в панель, где находится список.
При добавлении новой записи нужно только учесть случай, когда идет попытка повторного добавления пустой записи. Для этого достаточно проверить, что в последней записи displayName не null . В диалоге пустую запись лучше отображать специальной строкой, например, (---???---). Изменим для этого метод toString в классе Person :
public String toString() {
return (displayName == null ? "---???---" : displayName);
}
