Facebook google+ e-mail
Учимся программировать на JAVA
Практикум по программированию на языке Java

Занятие 9. Диалоговые окна (начало)

Шаг за шагом мы потихоньку «подбираемся» к очередной и чрезвычайно важной теме – обработке событий. Но для того, чтобы эти самые события не показались вам возникшими невесть откуда и зачем, нам надо обсудить еще один важный компонент графических интерфейсов – диалоговые окна. Это тем более уместно, что диалоговые окна – неотъемлемый элемент графического пользовательского интерфейса любого мало-мальски сложного приложения. Эти окна присутствуют повсеместно и должны быть вам хорошо знакомы. Например, диалоговыми являются окно выбора принтера, окно параметров, окно для поиска и замены текста и т.д.

Основные отличия диалогового окна (ему, как нетрудно догадаться, в библиотеке swing соответствует класс JDialog) от уже знакомого нам обычного окна (JFrame) два:

  1. диалоговые окна могут быть модальными. Это означает что диалоговое окно переключает на себя всю работу пользователя в приложении и до тех пор пока оно активно, доступ к оставшимся частям приложения заблокирован. Это свойство можно и отключить, но, как правило, этого не делают, т.к. весь смысл диалогового окна – это «заставить» пользователя сделать какую-то работу, какой-то выбор
  2. при создании и вызове диалогового окна можно указывать «родительское» окно, т.е. окно из которого это окно было сформировано и вызвано. За редкими исключениями диалоговые окна являются вспомогательными по отношению к тому окну из которого они были вызваны. Часто достаточно указать пустую ссылку (null – т.е. «ничего», «пусто», «не имеет значения»), но можно передать и действительную ссылку на родительское окно. Мы пока не будем усложнять себе жизнь и воспользуемся самым простым способом (см.ниже).

Кроме того, как правило у диалоговых окон отключают возможность изменять свои размеры (этого можно и не делать, но никакого смысла в этом нет) и, кроме того, их нельзя свернуть – диалоговые окна «вынуждают» пользователя работать с ними, что, в общем, и правильно.

А теперь немного попрограммируем: мы изменим наш исходный код и добавим в него вызов диалогового окна (вы можете скачать новый вариант программы – в котором уже два файла исходных кодов - по ссылке).

Прежде всего, посмотрите на код класса MoneyForNothing; в нем кое-что изменилось (изменения выделены жирным шрифтом):

...

public MoneyForNothing () {

     setTitle ("Добро пожаловать в Money for Nothing");

     setSize (new Dimension (600, 400));

     Dimension sSize = Toolkit.getDefaultToolkit ().getScreenSize (),

                   fSize = getSize ();

     if (fSize.height > sSize.height) {fSize.height = sSize.height;}

     if (fSize.width > sSize.width) {fSize.width = sSize.width;}

     setLocation ((sSize.width - fSize.width)/2,

                      (sSize.height - fSize.height)/2);

     setDefaultCloseOperation (DO_NOTHING_ON_CLOSE);

     setVisible (true);

 

     // Обработать событие закрытия окна

     addWindowListener (new WindowAdapter () {

         public void windowClosing (WindowEvent e) {

             ConfirmExitDialog exitApp = new ConfirmExitDialog (null);

             exitApp.setVisible (true);

             if (exitApp.getExitFlag ()) {

                 dispose ();

                 System.exit (0);

             }

         }

     });

}

...

Прежде всего, изменено поведение по умолчанию для системной кнопки закрытия окна – для него назначено поведение DO_NOTHING_ON_CLOSE (т.е. ничего не делать при закрытии). Как же теперь завершить работу приложения, что делать после того как пользователь нажмет эту кнопку? Хотя мы и запретили непосредственное закрытие окна этой кнопкой, после нажатия на нее генерируется событие, т.е. некая информация, сигнализирующая о том, что что-то и где-то произошло. События и их обработка – это ключевой элемент работы с графическими интерфейсами и мы вскоре ими займемся вплотную. А пока (см.код выше) скажем только, что для принятия решения – завершать работу приложения или нет – мы вводим дополнительный элемент: диалоговое окно подтверждения, описанное в классе ConfirmExitDialog. В этом классе есть метод getExitFlag (), возвращающий логическое значение (истина – true или ложь – false). Если возвращается истина – основное окно закрывается и приложение завершает свою работу; если ложь – приложение остается активным.

Создание и вывод на экран диалогового окна производится стандартно:

ConfirmExitDialog exitApp = new ConfirmDialog (null);

exitApp.setVisible (true);

Ниже этого фрагмента кода производится проверка результата, возвращаемого методом getExitFlag () из класса ConfirmExitDialog. Если этот метод возвращает true, то освобождаются все занятые приложением ресурсы (метод dispose ()) и выход.

Откомпилируйте исходный код и запустите его на исполнение после чего нажмите системную кнопку закрытия окна. Вы должны увидеть следующее диалоговое окно:

Программируем на Java. Диалоговые окна

Если вы выберете кнопку «Нет» или просто закроете диалоговое окно кнопкой закрытия окна, то в обработчик событий главного окна приложения будет возвращено значение false, если же вы выберете «Да», то в обработчик событий главного окна будет возвращено значение true и работа приложения будет завершена.

Заметьте, что само диалоговое окно выводится в центре родительского окна (если только вы не передвигали его) и, кроме того, является модальным: до тех пор пока вы не сделаете в нем выбор, продолжить работу с приложением вы не сможете.

А теперь мы рассмотрим «изнутри» класс ConfirmExitDialog, но прежде ответим на вопрос «А зачем вообще нужно подтвержение? Почему не закрывать приложение как раньше, без этих сложностей?». Ответ простой: такой способ завершения работы с приложением позволяет выполнить дополнительные действия, например, разорвать соединения с базами данных, сохранить измененный файл, послать сообщение и проч. Обычное завершение работы приложения (т.е. без подтверждения) на самом деле применяется не часто, так что привыкайте к такому порядку действий: это убережет ваши данные от искажения или разрушения.

А теперь запустите FAR и откройте файл ConfirmExitDialog.java: будем разбираться.

На первый взгляд содержимое файла кажется сложным, но на самом деле все не так страшно. Прежде всего, обратите внимание, что мы импортируем пакет java.awt.event.*. В этом пакете собраны классы обработки событий. Их там много, но пока это мы пропустим. Далее, наше диалоговое окно наследует поведение стандартного диалогового окна JDialog, которое, конечно, же содержится в пакете javax.swing.*. Пока все очень напоминает код для обычного окна JFrame.

Далее, мы объявляем три поля:

private boolean exitFlag = true;

private JButton okButton = new JButton ("Да"),

                 closeButton = new JButton ("Нет");

Первое поле – флаг, сигнализирующий о том, нужно ли завершать работу приложения или нет (об этом мы рассказывали чуть выше). Два оставшихся – объявление кнопок с соответствующими метками. Думается, что вы без труда поймете как именно описываются кнопки.

Переходим к конструктору. Здесь есть кое-что с чем мы еще не сталкивались:

public ConfirmExitDialog (JFrame parent) {

     super (parent, "Выход", true);

     getContentPane ().add (createGUI ());

     pack ();

     setSize (getPreferredSize ());

     setResizable (false);

     setLocationRelativeTo (parent);

 

     // Обработчики событий мыши

     okButton.addActionListener (new ActionListener () {

         public void actionPerformed (ActionEvent e) {

             exitFlag = true;

             dispose ();

         }

     });

     closeButton.addActionListener (new ActionListener () {

         public void actionPerformed (ActionEvent e) {

             exitFlag = false;

             dispose ();

         }

     });

     // Обработчик события закрытия диалога

     addWindowListener (new WindowAdapter () {

         public void windowClosing (WindowEvent e) {

             exitFlag = false;

             dispose ();

         }

     });

}

Во-первых, мы используем конструктор с параметром. В данном случае параметр – это ссылка на родительское окно, т.е. на окно, из которого был вызван диалог (вспомните, недавно мы об этом говорили). Т.к. вызов диалогового окна осуществлялся из JFrame, то, разумеется, тип параметра тоже JFrame.

Потом вы видите конструкцию super (кстати, это - зарезервированное слово Java). Вспомните, наш диалог наследуется от JDialog, описанного в пакете java.swing.*. Назначение super – вызвать методы, находящиеся в предке. Это очень полезная конструкция, т.к. позволяет воспользоваться «наследством» доставшимся от предка, причем именно тем, что вам нужно. В данном случае super обращается к конструктору предка с тремя параметрами: ссылкой на родителя (parent), заголовком окна («Выход») и признаком модальности (будет ли диалоговое окно модальным или нет – см. выше).

Чуть ниже по исходному коду диалоговому окну задаются размеры, указывается, что не допускается изменять его размеры (setResizable (false)) и то, что окно располагается в центре относительно окна-предка JFrame. После идут обработчики событий, но о них разговор особый, а мы пока продолжим разбираться с остальным кодом.

Тут мы настоятельно рекомендуем обратиться к документации и самостоятельно почитать о том, что мы сейчас рассказали «на пальцах»:

Программируем на Java. Диалоговые окна

Возможно, у вас уже возник вопрос: а заполняется содержимым само диалоговое окно, как компоненты (кнопки и проч.) добавляются в него? Операции по наполнению содержимого диалогового окна компонентами осуществляются в методе createGUI (). Этот метод (чуть позже мы рассмотрим его подробно) формирует и возвращает панель (грубо говоря – прямоугольник), на которой в нужных местах расположены все нужные компоненты. Эта панель добавляется к изначально пустому содержимому диалогового окна:

getContentPane ().add (createGUI ());

Здесь, метод getContentPane () возвращает панель содержимого самого диалогового окна , т.е. панель, служащая для размещения компонентов интерфейса. Панель содержимого существует всегда. Тут мы касаемся сложного и интересного вопроса архитектуры swing. Не останавливаясь на деталях, которые увели бы нас далеко в сторону, отметим пока только то, что панель содержимого (а также ряд других панелей) создается самой библиотекой swing специально для размещения ваших компонентов.

Таким образом, мы формируем интерфейс послойно: панель на панель и т.д. В основе всего лежит системная панель содержимого, на которую накладываются панели, созданные вами, на которых размещаются компоненты пользовательского интерфейса.

Для простых интерфейсов, вроде того, что вы видите, использование отдельного метода для формирования интерфейса, возможно, и лишнее, но для сложных, насыщенных элементами интерфейсов лучше создавать отдельный метод и не «загрязнять» конструктор.

Метод CreateGUI () выглядит так:

// Сформировать панель с элементами GUI

private JPanel createGUI () {

     JPanel mainPanel = new JPanel (new BorderLayout ()),

         lblPanel = new JPanel (new FlowLayout ()),

         botPanel = new JPanel (new FlowLayout (FlowLayout.RIGHT));

     JLabel confirmLabel = new JLabel ("Завершить работу приложения?",

                                                  JLabel.CENTER);

     lblPanel.setBorder (BorderFactory.createEtchedBorder ());

     lblPanel.add (confirmLabel);

     botPanel.add (closeButton);

     botPanel.add (okButton);

     mainPanel.add (lblPanel, BorderLayout.CENTER);

     mainPanel.add (botPanel, BorderLayout.SOUTH);

     return mainPanel;

}

Давайте пока на этом остановимся и продолжим на следующем занятии. Чтобы ваше ожидание следующего занятие было продуктивным, откройте документацию и поищите информацию о JPanel, JLabel и менеджерах размещения BorderLayout и FlowLayout. Мы обязательно расскажем о них, но вам следует попробовать понять их самостоятельно. Это важно сделать именно сейчас, «на свежую голову». Никакие занятия не могут рассказать все и обо всем; до многого нужно дойти и самому. А лучшего способа, чем чтение документации и эксперимент – просто не существует.

 

Занятие 8. Схема «Модель/Вид/Контроллер» (MVC)

 

Занятие 10. Диалоговые окна (окончание). События (знакомство)

 

Автор: Alex Tuesday

 

Все уроки ... 03.03.2014