События от панели controlPanel. 1 страница

Проверяя, какой именно кнопке равен объект btn, необходимо выполнить следующие действия: при нажатии на кнопки btnPrev, btnNext, btnBackgr, btnForegr, btnFont отобразить соответствующую страницу «блокнота». Проверка равенства объекта btn другому объекту осуществляется следующим образом:

if(btn.equals(btnPrev)) // от кнопки "Previous"?

{ // обработка события от кнопки "Previous"

// .........................................................

}

Если нажата кнопка btnPrev, то сначала с помощью метода getLayout() получаем ссылку на используемый для панели cardPanel менеджер компоновки, а затем вызываем метод previous() для отображения предыдущей страницы панели cardPanel:

CardLayout cl=(CardLayout)cardPanel.getLayout();

cl.previous(cardPanel);

Аналогичные действия сделаем, если нажата кнопка btnNext, но для отображения следующей страницы вызовем метод next() класса CardLayout.

При нажатии на кнопки btnBackgr, btnForegr, btnFont для отображения соответствующей страницы «блокнота» используется метод show() класса CardLayout. Например, если нажата кнопка btnBackgr:

CardLayout cl=(CardLayout)cardPanel.getLayout();

cl.show(cardPanel,"Background");

События от панели cardPanel.

Проверяя, какому именно меню равен объект ch, необходимо выполнить следующие действия: изменить цвет фона или цвет переднего плана панели drawPanel или изменить шрифт для текста. Проверка равенства объекта ch другому объекту осуществляется следующим образом:

if(ch.equals(chBackgr)) // от меню выбора цвета фона?

{ // обработка события от меню выбора цвета фона

// ...........................................

}

Если событие выбора пункта пришло от меню chBackgr, то, в зависимости от номера выбранного пункта, установим новый цвет фона панели drawPanel при помощи метода setBackground(), перерисуем панель и окно апплета:

if(index==0) drawPanel.setBackground(Color.yellow);

else if(index==1) drawPanel.setBackground(Color.green);

else if(index==2) drawPanel.setBackground(Color.white);

drawPanel.repaint(); repaint();

Если событие выбора пункта пришло от меню chForegr, то, в зависимости от номера выбранного пункта, установим новый цвет переднего плана панели drawPanel при помощи метода setForeground(), перерисуем панель и окно апплета.

И, наконец, если событие выбора пункта пришло от меню chFont, то, в зависимости от номера выбранного пункта, запишем в строку fontName имя выбранного шрифта, перерисуем панель и окно апплета.

6. Приложение WindowsDemo

Задание. Создать апплет WindowsDemo, в окне которого расположены две кнопки с названиями «Show Frame Window» и «Hide Frame Window». Они предназначены для отображения и временного сокрытия окна класса FrameWnd (новый подкласс класса Frame), в котором выводится строка «This window - object of FrameWnd».В окне класса FrameWnd создается главное меню, содержащее меню «File» (пункты «New» и «Exit») и «Help» (пункты «Content» и «About»), а также в окно добавляется кнопка «Ok». При нажатии на кнопку «Ok» фрейма и при закрытии окна фрейма пользователем происходит его временное сокрытие, а при выборе пункта «Exit» меню фрейма завершается работа всего приложения. При выборе пунктов «New», «Content» и «About» меню окна класса FrameWnd появляется окно диалоговой панели класса DialogWnd (новый подкласс класса Dialog), в которой отображается название выбранного пункта и кнопка «Ok», нажатие на которую вызывает закрытие диалогового окна.

Методические указания.Апплет должен быть создан на основе шаблонов, содержащихся в Приложении 1(или при помощи системы Java Applet Wizard).

Создание подкласса класса Frame.

Создадим новый подкласс FrameWnd класса Frame (в java-файле апплета), переопределив необходимые методы суперкласса:

class FrameWnd extends Frame // объявление нового класса фрейма

{ public FrameWnd(String sTitle) // sTitle - заголовок

{ super(sTitle); // для правильного создания окна фрейма

resize(320,240); // задание необходимых размеров окна

// здесь можно определить различные параметры фрейма,

// например, форму курсора, меню, добавить компоненты и др.

// ..................

}

public boolean handleEvent(Event evt)

{ switch (evt.id)

{ case Event.WINDOW_DESTROY:

hide(); // временное сокрытие окна фрейма

return true;

case Event.ACTION_EVENT:

// обработка событий от кнопки

if(evt.target instanceof Button)

{ // получаем ссылку на кнопку

Button btn=(Button)evt.target;

// проверка, от какой именно кнопки пришло

// событие и обработка события

// ......................................

return true;

}

// обработка событий от меню

if(evt.target instanceof MenuItem)

{ // получаем название/метку элемента меню

String label=(String)evt.arg;

// проверка, от какого именно пункта пришло

// событие и обработка события

// ......................................

return true;

}

break;

default: return super.handleEvent(evt);

}

return true;

}

public void paint(Graphics g)

{ // вывод в окно фрейма

// ....................................

super.paint(g); // вызов метода родительского класса

}

}

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

Создание подкласса класса Dialog.

Создадим новый подкласс DialogWnd класса Dialog (в java-файле апплета), переопределив необходимые методы суперкласса:

class DialogWnd extends Dialog // объявление нового класса диалога

{ // sMsg - сообщение, sTitle - заголовок окна,

// parent - родительский фрейм, modal - модальность

public DialogWnd(String sMsg,Frame parent,String sTitle,boolean modal)

{ super(parent,sTitle,modal); // для правильного создания диалога

resize(100,100); // задание необходимых размеров окна диалога

// здесь можно создать все необходимые компоненты

// для размещения внутри диалоговой панели

// .......................................

}

public boolean handleEvent(Event evt)

{ switch (evt.id)

{ case Event.WINDOW_DESTROY:

dispose(); // удаление окна диалога

return true;

case Event.ACTION_EVENT:

// обработка событий от кнопки

if(evt.target instanceof Button)

{ // получаем ссылку на кнопку

Button btn=(Button)evt.target;

// проверка, от какой именно кнопки пришло

// событие и обработка события

// ...........................

return true;

}

break;

default: return super.handleEvent(evt);

}

return true;

}

}

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

Класс WindowsDemo. Объявление элементов класса.

В классе апплета объявим следующие элементы - ссылки на объекты классов:

FrameWnd frmWnd; // окно фрейма

Button btnShow; // кнопка для отображения фрейма

Button btnHide; // кнопка для сокрытия фрейма

Ссылки на экземпляры соответствующих классов будут присвоены этим переменным в методе init() апплета.

Класс WindowsDemo. Инициализация ( метод init()).

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

Сначала создадим кнопки btnShow и btnHide с надписями «Show Frame Window» и «Hide Frame Window» соответственно, а затем добавим их в апплет методом add().

Далее создадим объект нового класса FrameWnd - фрейм с заголовком «Frame from Applet» и ссылку на него присвоим переменной frmWnd.

Класс WindowsDemo. Завершение работы ( метод destroy()).

При завершении работы апплета удаляем созданное окно фрейма и освобождаем связанные с ним ресурсы:

frmWnd.dispose();

Класс WindowsDemo. Отрисовка содержимого окна ( метод paint()).

Так как апплет не выводит в окно никаких графических изображений, то в этом методе не делается никаких действий.

Класс WindowsDemo. Обработка событий ( метод action()).

Переопределим в класса апплета метод action(), вызываемый при некоторых действиях пользователя. В данном случае он будет обрабатывать события, поступающие от кнопок. Сначала создадим шаблон этого метода:

public boolean action(Event evt, Object obj)

{

// обработка событий от кнопок

if(evt.target instanceof Button) // объект является кнопкой?

{ // получаем ссылку на кнопку, вызвавшую событие

Button btn=(Button)evt.target;

// проверка, от какой именно кнопки пришло событие

// и обработка события от этой кнопки

// ...................................

return true; // если событие обработано, иначе return false

}

// необработанные события передаем на дальнейшую обработку

return false;

}

Проверяя, какой именно кнопке равен объект btn, необходимо выполнить следующие действия: при нажатии на кнопку btnShow отобразить фрейм frmWnd методом show(), а при нажатии на кнопку btnHide скрыть его методом hide(). Проверка равенства объекта btn другому объекту осуществляется следующим образом:

if(btn.equals(btnShow)) // от кнопки btnShow?

{ // обработка события от кнопки btnShow

// ....................................

}

Класс FrameWnd. Объявление элементов класса.

В классе фрейма объявим следующие элементы - ссылки на объекты классов:

Button btnOk; // кнопка сокрытия фрейма

MenuBar mainMenu; // полоса меню

Menu fileMenu; // меню "File"

Menu helpMenu; // меню "Help"

Ссылки на экземпляры соответствующих классов будут присвоены этим переменным в конструкторе фрейма.

Класс FrameWnd. Создание объекта (конструктор FrameWnd()).

Добавим в конструктор класса следующие действия. Сначала установим методом setBackground() цвет Color.yellow в качестве фона окна. Цвет переднего плана установим методом setForeground(), передавая ему константу Color.black.

Затем установим для окна фрейма режим размещения компонент FlowLayout при помощи метода setLayout(). Создадим кнопку btnOk с надписью «Ok» и введем ее во фрейм методом add().

Для того, чтобы сформировать меню фрейма, сначала создадим панель меню mainMenu - объект класса MenuBar, затем создадим выпадающие меню fileMenu (строка "File") и helpMenu (строка "Help") - объекты класса Menu, которые потом добавим в mainMenu его методом add(). Подготовленную панель меню mainMenu установим в качестве главного меню фрейма методом фрейма setMenuBar().

В выпадающие меню fileMenu и helpMenu добавим элементы "New", "Exit" и "Content", About" - объекты класса MenuItem.

Класс FrameWnd. Отрисовка содержимого окна ( метод paint()).

В этом методе просто выведем в окно фрейма строку «This window - object of FrameWnd» методом drawString() контекста отображения.

Класс FrameWnd. Обработка событий ( метод handleEvent()).

Добавим в метод handleEvent() обработку событий, поступающих от кнопки btnOk и от меню фрейма.

Если объект, вызвавший событие, является кнопкой (результат операции evt.target instanceof Button равен true) и эта кнопка является кнопкой btnOk (метод btn.equals(btnOk) возвращает true), то при помощи метода hide() временно скроем окно фрейма.

Если объект, вызвавший событие, является элементом меню (результат операции evt.target instanceof MenuItem равен true), то:

· если выбран элемент меню "Exit" (объект label равен строке "Exit"), то методом System.exit(0) прекратим работу всего апплета Проверка равенства объекта label другому объекту осуществляется следующим образом:

if(label.equals("Exit")) // от элемента "Exit"?

{ // обработка события

// .....................

}

· если же выбраны элементы "New", "Content" или About", то в каждом случае создадим модальную диалоговую панель класса DialogWnd, родителем которой является текущий экземпляр фрейма, в которой выводится название выбранного элемента меню, и отобразим ее, например:

DialogWnd dlgWnd=new DialogWnd("Item New selected",

this,"Dialog from Frame",true);

dlgWnd.show();

Класс DialogWnd. Объявление элементов класса.

В классе диалога объявим следующие элементы - ссылки на объекты классов:

Label lbMsg; // поле для отображения текста сообщения

Button btnOk; // кнопка удаления панели

Ссылки на экземпляры соответствующих классов будут присвоены этим переменным в конструкторе фрейма.

Класс DialogWnd. Создание объекта (конструктор DialogWnd()).

Добавим в конструктор класса следующие действия. Сначала установим для окна диалога режим размещения компонент GridLayout (сетка 2x1) при помощи метода setLayout(). Затем создадим метку lbMsg, передавая конструктору класса Label строку sMsg и режим выравнивания по центру. Созданную метку введем в панель диалога методом add(). Создадим кнопку btnOk с надписью «Ok» и введем ее в диалог методом add().

Класс DialogWnd. Обработка событий ( метод handleEvent()).

Добавим в метод handleEvent() обработку событий, поступающих от кнопки btnOk. Если объект, вызвавший событие, является кнопкой (результат операции evt.target instanceof Button равен true) и эта кнопка является кнопкой btnOk (метод btn.equals(btnOk) возвращает true), то при помощи метода dispose() удалим окно диалога.

Задания к лабораторной работе

Задание 1. Создать апплеты PanelsDemo1,PanelsDemo2и WindowsDemo и объяснить их работу. Первые два апплета должны иметь возможность работать как независимые приложения.

Задание 2. Дать ответы на контрольные вопросы

Контрольные вопросы

25. Что такое контейнеры? Какие два основных вида контейнеров cуществует?

26. Какие существуют типы контейнеров?

27. Для чего чаще всего используются панели?

28. Какими двумя способами можно рисовать в окне панели?

29. Как происходит обработка события компонентами контейнера?

30. Какова иерархия обработки события различными компонентами контейнера?

31. В чем основное отличие окон и панелей?

32. Что является обязательным параметром конструктора при создании экземпляра класса окон?

33. Каковы отличительные особенности имеют фреймы?

34. Какие методы должны быть переопределены в новом подклассе фреймов?

35. Какой класс контейнеров автоматически поддерживает работу с меню? Почему он это делает автоматически?

36. Как добавить меню в контейнер?

37. Как создать новое меню и добавить в него элементы?

38. Какие существуют классы элементов меню?

39. Какими методами обрабатываются события меню?

40. Для чего в основном используются окна диалогов?

41. Каковы важные отличия окон диалогов от фреймов?

42. Объект какого класса должен обязательно быть родителем диалогового окна?

43. Как создать диалог своего класса?

44. Для чего предназначены менеджеры компоновки? Какие существуют режимы размещения?

 

ЛАБОРАТОРНАЯ РАБОТА № 6.

МНОГОПОТОКОВЫЕ ПРИЛОЖЕНИЯ (2 часа)

МЕТОДИЧЕСКИЕ УКАЗАНИЯ К ЛАБОРАТОРНОЙ РАБОТЕ

При создании приложений для операционной системы Microsoft Windows с использованием программного интерфейса Win API операционной системы можно решать многие такие задачи, как анимация или работа в сети, и без использования многозадачности (без создания подзадач главной задачи). Например, для анимации можно обрабатывать сообщения соответствующим образом настроенного таймера.

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

Прежде чем начать рассматривать процесс реализации мультизадачности, уточним некоторые термины.

1. Процессы, задачи и приоритеты

Обычно в мультизадачной операционной системе выделяют такие объекты, как процессы и задачи. Между этими понятиями существует большая разница, которую следует четко представлять.

Процесс (задача)

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

Поток (нить)

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

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

Приоритеты потоков

Если процесс создал несколько потоков, то все они выполняются параллельно, причем время центрального процессора (или нескольких центральных процессоров в мульпроцессорных системах) распределяется между этими потоками.

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

Следует особо отметить, что распределение времени выполняется для потоков, а не

для процессов. Потоки, созданные разными процессами, конкурируют между собой за получение процессорного времени. Каждому потоку задается приоритет его выполнения, уровень которого определяет очередность выполнения того или иного потока.

2. Реализация многозадачности в Java

Для создания мультизадачных приложений Java необходимо воспользоваться классом java.lang.Thread. В этом классе определены все методы, необходимые для создания потоков, управления их состоянием и синхронизации.

Есть две возможности использования класса Thread.

Во-первых, можно создать собственный класс на базе класса Thread. При этом необходимо переопределить метод run(). Новая реализация этого метода будет работать в рамках отдельного потока.

Во-вторых, создаваемый класс, не являясь подклассом класса Thread, может реализовать интерфейс Runnable. При этом в рамка этого класса необходимо определить метод run(), который будет работать как отдельный поток.

Рассмотрим подробнее обе возможности реализации многозадачности.

2.1 Создание подкласса Thread

Первый способ реализации мультизадачности основан на наследовании от класса Thread. При использовании этого способа для потоков определяется отдельный класс, например:

class myThread extends Thread

{ // этот метод получает управление при запуске

// потока методом start() класса Thread

public void run()

{ // здесь можно добавить код, который будет

// выполняться в рамках отдельного потока

}

// здесь можно добавить специализированный для класса код

}

Следует обратить внимание на метод run(). Этот метод должен быть всегда переопределен в классе, наследованном от Thread. Именно он определяет действия, выполняемые в рамках отдельного потока. Если поток используется для выполнения циклической работы, этот метод содержит внутри себя бесконечный цикл.

Метод run() не вызывается напрямую никакими другими методами. Он получает управление при запуске потока методом start() класса Thread. В случае апплетов создание и запуск потоков обычно осуществляется в методе start() апплета.

Остановка работающего потока выполняется методом stop() класса Thread. Обычно остановка всех работающих потоков, созданных апплетом, выполняется в методе stop() апплета.

Рассмотрим процедуру создания и запуска потоков на примере использования их в апплетах (в обычных приложениях потоки используются аналогично)- пример 1. Определим класс апплетов MultiTask (можно использовать шаблоны, содержащиеся в Приложении 1):

/*------------- Пример 1. Файл MultiTask.java -------------*/

import java.applet.*;

import java.awt.*;

public class MultiTask extends Applet

{ public MultiTask()

{ // здесь можно добавить код конструктора

}

public String getAppletInfo()

{ return "Name: Applet\r\n"; // информация об апплете

}

public void init()

{ resize(320, 240); // установка размера апплета

// здесь можно добавить код инициализации апплета

}

public void destroy()

{ // здесь можно добавить код завершения работы апплета

}

public void paint(Graphics g)

{ // здесь можно добавить код вывода в окно апплета

}

public void start()

{ // здесь можно добавить код, работающий при запуске апплета

}

public void stop()

{ // здесь можно добавить код, работающий при остановке

}

// здесь можно добавить специализированный для класса код

}

Перед использованием других потоков необходимо определить их классы, например:

// поток, рисующий прямоугольники в окне апплета

class DrawRects extends Thread

{ // конструктор, получающий ссылку на создателя объекта - апплет

public DrawRects(MultiTask parentObj)

{ parent=parentObj;

}

public void run()

{ Graphics gr=parent.getGraphics(); // контекст апплета

while(true) // в цикле выводятся прямоугольники

{ int w=parent.size().width-1, h=parent.size().height-1;

gr.setColor(new Color((float)Math.random(),

(float)Math.random(),(float)Math.random()));

gr.fillRect((int)(Math.random()*w),(int)(Math.random()*h),

(int)(Math.random()*w),(int)(Math.random()*h));

}

}

MultiTask parent; // ссылка на создателя объекта

}

// поток, рисующий эллипсы в окне апплета

class DrawOvals extends Thread

{ // конструктор, получающий ссылку на создателя объекта - апплет

public DrawOvals(MultiTask parentObj)

{ parent=parentObj;

}

public void run()

{ Graphics gr=parent.getGraphics(); // контекст апплета

while(true) // в цикле выводятся эллипсы

{ int w=parent.size().width-1, h=parent.size().height-1;

gr.setColor(new Color((float)Math.random(),

(float)Math.random(),(float)Math.random()));

gr.fillOval((int)(Math.random()*w),(int)(Math.random()*h),

(int)(Math.random()*w),(int)(Math.random()*h));

}

}

MultiTask parent; // ссылка на создателя объекта

}

Теперь рассмотрим процесс использования созданных классов. Вначале апплет должен объявить в качестве своих элементов ссылки на объекты классов потоков:

DrawRects Rects =null;

DrawOvals Ovals =null;

Далее в методе start() апплета создаются объекты потоков и потоки запускаются на выполнение:

public void start()

{ if(Rects==null) { Rects=new DrawRects(this); Rects.start(); }

if(Ovals==null) { Ovals=new DrawOvals(this); Ovals.start(); }

}

Когда пользователь покидает страницу апплета, то вызывается метод stop() апплета. Именно в этом методе целесообразно останавливать работающие потоки:

public void stop()

{ if(Rects!=null) Rects.stop(); if(Ovals!=null) Ovals.stop();

}

2.2 Реализация интерфейса Runnable

Если нет необходимости расширять класс Thread подобно примерам, показанным выше, то можно применить второй способ реализации многозадачности. Допустим, уже существует класс MyClass, функциональные возможности которого удовлетворяют разработчика:

class myClass

{ // код класса - объявление его элементов и методов

..................................................

}

и теперь необходимо, чтобы он выполнялся как отдельный поток. Для этого необходимо для этого класса реализовать интерфейс Runnable, создавая разделяемый метод run() интерфейса Runnable:

class myClass implements Runnable

{ // код класса - объявление его элементов и методов

..................................................

// этот метод получает управление при запуске потока

public void run()

{ // здесь можно добавить код, который будет

// выполняться в рамках отдельного потока

}

}

Рассмотрим пример и продолжим создание апплета MultiTask. Пусть существует класс, предназначенный для вывода случайной линии в окне апплета MultiTask:

class DrawLines

{ // конструктор, получающий ссылку на создателя объекта

public DrawLines(MultiTask parentObj)

{ parent=parentObj;

}

public void draw()

{ int w=parent.size().width-1, h=parent.size().height-1;

Graphics gr=parent.getGraphics();

gr.setColor(new Color((float)Math.random(),

(float)Math.random(),(float)Math.random()));

gr.drawLine((int)(Math.random()*w),(int)(Math.random()*h),

(int)(Math.random()*w),(int)(Math.random()*h));

}

MultiTask parent; // ссылка на создателя объекта

}

Реализуем для этого класса интерфейс Runnable и создадим метод run(), выполняющийся в рамках отдельного потока:

class DrawLines implements Runnable

{ // конструктор, получающий ссылку на создателя объекта

public DrawLines(MultiTask parentObj)

{ parent=parentObj;

}

public void draw()

{ int w=parent.size().width-1, h=parent.size().height-1;

Graphics gr=parent.getGraphics();

gr.setColor(new Color((float)Math.random(),

(float)Math.random(),(float)Math.random()));

gr.drawLine((int)(Math.random()*w),(int)(Math.random()*h),