Платформа программирования J2ME для портативных устройств

         

Диаграмма наследования компонентов



Системные свойства

CLDC/MIDP поддерживает системные свойства, которые являются парами «ключ-значение», представляющими информацию о платформе и среде, в которой выполняются приложения MIDP. Теоретически это тот же тип свойств, который вы найдете в J2SE. К сожалению, в CLDC/MIDP нет класса Java.util.Properties для облегчения вашей работы со свойствами.

Спецификация MIDP определяет только небольшой набор стандартных свойств, которые показаны в таблице 3.4. Реализации могут поддерживать дополнительные системные свойства определенных производителей, но необязательно. Вы должны знать о том, свойства какого производителя или платформы вы используете для того, чтобы предупреждать проблемы с мобильностью.

Как и приложения J2SE, приложения MIDP могут отыскивать системные свойства с помощью класса java.lang.System. Чтобы узнать значение свойства, используйте метод класса System

String getProperty(String key)

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



Если доступно более одного MIDлета



Рисунок 3.3. Если доступно более одного MID-лета, AMS выводит меню, показывая вам их все. AMS, а не ваше приложение, создает кнопку Launch (Запуск). Вы должны нажать на нее, чтобы запустить выбранный МЮ-лет




Этот MIDлет запускается с помощью



Рисунок 3.2. Добавьте новые MID-леты к набору с помощью закладки MIDIets (MID-леты) в окне Settings (Параметры)






Теперь создайте проект и затем выполните его. В это время вы увидите окно, показанное на рисунке 3.3. Заметьте, что теперь вы видите меню, которое показывает названия обоих MID-летов, находящихся в наборе MID-летов. Поскольку присутствует более одного MID-лета, который можно выполнить, AMS должна вывести меню и позволить вам выбрать тот, который вы хотите запустить. Конечно, эмулятор здесь принимает на себя роль AMS реального устройства.
На реальном устройстве AMS устройства показывает это меню. Например, телефоны Motorola и Siemens используют стандартные списки выбора, которые позволяют вам выбрать сначала AMS, затем набор MID-летов и, наконец, MID-лет. На чужих рынках (в Японии, например) телефоны могут иметь кнопку, помеченную «Web», которая запускает AMS и автоматически запускает Web-браузер, созданный на Java. Перечисленные MID-леты - это те, которые известны AMS.
Когда вы добавляете MID-лет к набору, вы сообщаете инструментарию, что вы хотите, чтобы новый MID-лет был доступен для выполнения. Когда вы создаете MID-лет, инструментарий размещает его файлы .class в файле JAR набора MID-летов и обновляет файлы манифеста и JAD. Этот порядок действий осуществляется в согласии со спецификацией J2ME, которая, как вы помните, требует, чтобы МГО-леты содержались в файле JAR. Выберите MID-лет HelloWorld и затем нажмите на экранную кнопку Launch (Запуск), чтобы выполнить его. На рисунке 3.4 показано одно окно, которое он создает и показывает.




Это MIDPверсия знакомой вам программы HelloWorld



Листинг 3.1. Это MIDP-версия знакомой вам программы HelloWorld

import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.midlet.MIDlet;
/**
Создание программы «Hello world» в J2ME MIDP.
Обратите внимание, что класс должен быть открытым, для того чтобы программа
управления приложениями устройства могла создать его экземпляр.
*/
public class HelloWorld extends MIDlet
{
// Displayable. Этот компонент отображается на экране, private Form form;
// Display. Этот объект управляет всеми компонентами
// Displayable.
private Display display;
// Открытый конструктор no-arg необходим, даже хотя система
// вызывает startAppO! AMS вызывает конструктор
// no-arg класса для создания экземпляра класса.
// Либо создайте открытый конструктор no-arg, либо
// объявите об отсутствии конструкторов и позвольте
// компилятору создать открытый конструктор no-arg. public HelloWorldO
{
super () ; I
public void destroyApp(boolean destroy)
form = null;
notifyDestroyed();

}
public void pauseAppO
}
public void startAppf)
// Создайте элемент Displayable. form = new Form (."Hello, World");

// Добавьте строку в форму. String msg = "My first MIDlet!"; form.append(msg);

// Это приложение просто выводит на экран одну форму, созданную выше.
display = Display.getDisplay (this) ;:
display.setCurrent(form);

}
}

Во-первых, отметьте, что это приложение описывает класс, называемый HelloWorld, который дополняет javax.microedition.midlet.MIDlet. Все MID-леты должны дополнять этот класс.

Класс HelloWorld является основным классом вашего приложения. По этой причине он должен быть объявлен открытым (public). Более того, вы должны объявить открытый безаргументный (no-argument) конструктор или убедиться, что конструкторов нет, в этом случае компилятор определит безаргументный конструктор для вас. Те читатели, которые знакомы с апплетами Java, найдут сходство между моделями управления жизненным циклом апплета и MID-лета.

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

AMS затем вызовет метод startAppO. В листинге 3.1 методы startApp(), pauseApp() и destroyApp() подменяют абстрактные объявления класса MIDlet. Обратите внимание на то, что все кода инициализации входят в метод startApp (), а не в конструктор. Вы, естественно, можете добавить какой-либо код инициализации в ваш конструктор, он будет выполнен перед вызовом startApp(). Однако метод startApp() всегда вызывается в качестве входной точки вашего MID-лета.

А как насчет метода main ()? Определение языка Java требует, чтобы все приложения на Java имели метод main () со следующей сигнатурой:

public static void main(String [] args)

Если приложения на J2ME являются настоящими приложениями Java, а это требование я оговаривал ранее, тогда где-то должен быть главный метод, который является реальной точкой входа, используемой виртуальной машиной для запуска процесса выполнения приложения. В действительности существует такой метод. Это часть реализации MIDP (не приложения), и, обычно, программное обеспечение AMS вызывает его. Программа AMS обрабатывает запросы вызова приложения, например, порождая подпроцесс нити запроса запуска каждого MID-лета и контролируя MID-лет из этой нити. Реальные детали зависят от продукта. В J2ME Wireless Toolkit компании «Sun» класс com.sun.midp.Main определяет метод main().

Метод startApp() создает объект, называемый формой, и пересылает в конструктор строку, которая представляет из себя название формы. Форма - это экземпляр класса javax.microedition.lcdui.Form, который является разновидностью экрана, отображаемого на вашем дисплее. Она имеет такое название, поскольку ее функции в чем-то сходны с функциями формы HTML - она содержит один или более визуальных элементов, таких как строки.

Затем метод startApp() создает стандартный объект String и добавляет его в форму. Он затем получает ссылку на объект, называемый display, и устанавливает объект формы в качестве текущего отображаемого на дисплее объекта.

Когда этот код выполняется, вы видите экран, изображенный на рисунке 3.4. Когда вы нажимаете на кнопку с телефонной трубкой, которая сообщает устройству об отключении, AMS вызывает destroyApp(), который просто устраняет все ссылки на объект формы, созданные перед этим. Теперь наступает время сборки мусора. Затем AMS закрывает MID-лет.

Вы отвечаете за правильное расположение объектов, созданных вашими МЮ-летами. В случае с данным примером не должно иметь значения, задали ли вы то, что ссылки на формы изменяются до нуля, поскольку MID-лет был закрыт. Но, в общем, вам необходимо правильно управлять ссылками на объекты вашей программы, как и в любой программе Java.



MIDлеты имеют прямой



Листинг 3.2. MID-леты имеют прямой доступ ко всем четырем стандартным системным свойствам, определяемым спецификацией CLDC

import javax.microedition.Icdui.Display;
import javax.microedition.Icdui.Displayable;
import javax.microedition.Icdui.Form;
import javax.microedition.midlet.MIDlet;
/**
Создание программы «Hello world» в J2ME MIDP.
Заметьте, что класс должен быть открытым, для того чтобы программа
управления приложениями устройства могла создать его экземпляр.
*/
public class HelloWorld extends MIDlet
public void startApp()
{
// Создайте элемент Displayable. form = new Fo.rmC'Hello World");

// Добавьте строку в форму. String msg = "My first MIDlet!"; form.append(msg);

// Это приложение просто выводит на экран одну форму, созданную выше.
display = Display.getDisplay(this);

display.setCurrent(form);

printSystemPropertiesf) ;
/**
Вывести значения стандартных системных свойств
С помощью вызова System.getProperty().
*/
protected void printSystemProperties()
String conf;
String profiles; String platform; String encoding; String locale;
conf = System.getProperty("microedition.configuration") ;
System.out.println(conf);

profiles = System.getProperty("microedition.proflies");

System.out.println(profiles);

platform = System.getProperty("microedition.platform");

System.out.println(platform);

encoding = System.getProperty("microedition.encoding");

System.out.println(encoding);

locale = System.getProperty("microedition.locale");

System.out.println(locale);
System.out.println();

}
}

Обратите внимание на добавление вызова к методу printSystemProperties () в конце метода startApp(). Этот метод просто извлекает и отображает стандартные данные значений пяти стандартных системных свойств MIDP. Данные, которые программа записывает в стандартные результаты, показаны здесь:

CLDC-1 .0
MIDP-1.0
J2me
ISO-8859-1
enJJS

Четвертая строчка выходных данных просто воспроизводит набор знаков кодировки, который использует текущая реализация платформы CLDC/MIDP. Последняя строка выходных данных отражает текущую локализацию. Спецификации локализации включают две части: первая часть показывает языковые настройки, в то время как вторая часть показывает код страны. Международная организация по стандартизации (ISO) издает два стандарта, которые определяют набор приемлемых значений для языкового и странового кодов.

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



Измененный метод теперь



Листинг 3.3. Измененный метод теперь также выдает свойства приложения. Программное обеспечение AMS устройства управляет свойствами приложения.

public void startApp()
// Создайте элемент Displayable. form = new FormC'Hello, World")/'
// Добавьте строку в форму. String msg = "My-first MIDlet!"; form.append(msg);

// Это приложение просто выводит на экран одну форму,
// созданную выше.
display = Display.getDisplay (this) ;
display.setCurrentfform);

printSystemProperties () ; printAppProperties () ) ) ;
}

Метод, показанный в листинге 3.3, выдает значения стандартных свойств приложения MID-летa в стандартный результат. Листинг 3.4 показывает метод printAppProperties().



Атрибуты MIDлета или



Листинг 3.4. Атрибуты MID-лета, или свойства, отличаются от системных свойств. Вы можете описать неограниченное количество необязательных атрибутов MID-лета в дополнение к предварительно определенным, требуемым

/ * *
Вывести свойства приложения с помощью вызова
MIDlet.getAppProperty ().
*/
protected void printAppProperties ()
(
System.out.println(getAppProperty("MI Diet-Name"));

System.out.println(getAppProperty("MIDlet-Jar-Size"));

System, out. println (getAppProperty ("MI Diet-Jar-URL ")) ;
System.out.println(getAppProperty("MIDlet-Vendor"));

}

Эта последняя версия программы HelloWorld теперь выводит следующие строки в дополнение к стандартному результату, который вы можете видеть в окне основной консоли Wireless Toolkit. Метод printAppProperties () выводит последние четыре строки результата.

CLDC-1.0
MIDP-1.0
J2me
ISO-8859-1
en_US
HelloWorld 6781
HelloWorid.jar Vartan Piroumian

Четыре атрибута, выбранные в листинге 3.4, являются стандартными свойствами приложений, доступными для всех MID-летов. Однако вспомните главу 2 и то, что в таблице 2.4 описаны некоторые дополнительные обязательные атрибуты MID-лета. Также спецификация MIDP определяет некоторые необязательные дополнительные атрибуты, в таблице 2.5 перечислены эти необязательные атрибуты. Ваши приложения имеют доступ к ним ко всем через механизм, продемонстрированный в листингах 3.3 и 3.4.

Кроме того, MID-леты могут описывать необязательные зависимые от приложения атрибуты. Вы можете описать так много связанных с приложением свойств, сколько хотите. Ваше приложение будет затем получать к ним доступ с помощью метода MIDlet.getAppProperty(), показанного в листингах 3.3 и 3.4. Эта возможность является своего рода конфигурированием или механизмом настройки MID-летов. Вы увидите некоторые примеры выборочного описания атрибутов и их использования в главе 9.



MIDлет может находиться в одном



Рисунок 3.5. MID-лет может находиться в одном из трех состояний. Когда AMS впервые создает МЮ-лет, MID-лет существует в приостановленном состоянии


Программа управления приложениями помещает MID-лет в приостановленное состояние, вызывая метод pauseApp(). MID-лет может также запрашивать AMS о входе в приостановленное состояние, вызывая свой метод notifyPausedf). MID-лет может после этого запрашивать, чтобы он был помещен в активное состояние, вызывая resumeRequest ().

AMS может сигнализировать MID-лету, что он должен привести себя в порядок и подготовиться к тому, что он будет прерван с помощью вызова метода MID-лета destroyApp(). MID-лет может сигнализировать о завершении своего выполнения AMS, вызывая notifyDestroyed(). В таблице 3.2 перечислены методы класса javax.microedition.midlet.MIDlet, которые управляют состоянием MID-лета.



Модель компонентов пользовательского интерфейса MIDP



Модель компонентов пользовательского интерфейса MIDP

Компоненты пользовательского интерфейса MIDP определены в пакете javax.microedition.Icdui. Название этого пакета, возможно, изменится в будущих выпусках, поскольку его имя слишком близко связано с определенным типом физических устройств отображения. Компоненты пользовательского интерфейса MIDP составляют большинство классов в этом пакете. Понимание организации этих компонентов пользовательского интерфейса является наиважнейшим при создании приложений MIDP.

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

display = Display.getDisplay (this);
display.setCurrentl form);

Первая из двух показанных выше строчек получает ссылку на объект Display. Объект Display является вбъектом Java, который представляет физическое отображение экрана устройства. В следующей строчке говорится: «Сделать эту форму текущим отображаемым объектом».

Форма является одним из видов отображаемых компонентов, которые могут иметь визуальное представление. Отображаемый компонент в MIDP является верхнеуровневым компонентом пользовательского интерфейса. Верхнеуровневые компоненты могут быть самостоятельно отображены MID-летом. То есть они не нуждаются в том, чтобы содержаться внутри любого другого компонента - в действительности они и не могут. Приложение MIDP может отображать только один компонент верхнего уровня за раз.

Для программистов на AWT и Swing компонент верхнего уровня MIDP эквивалентен java.awt.Frame или java.awt.Window в инструментариях Swing и AWT. Продукт MIDP управляет компонентами верхнего уровня так же, что и собственная система окон управляет Window в реализации платформы J2SE.

Когда AMS запускает MID-лет, она делает следующее:

Она задает класс Display. Она связывает экземпляр Display с вашим экземпляром MIDlet.

Ваша программа никогда не создает объект Display, а реализация MIDP делает это. Ваш MID-лет создает только компоненты пользовательского интерфейса - экземпляры конкретного подкласса Displayable или Item, которые будут отображены на экране в течение жизненного цикла вашего MID-лета. Вы сообщаете объекту Display, когда показывать ваши отображаемые компоненты, вызывая метод Display.setCurrent().

Здесь взаимодействуют три основных объекта: ваш экземпляр MIDlet, экземпляр Display, созданный AMS, и компонент Displayable, который вы хотите отобразить на экране. На рисунке 3.6 показана диаграмма связей между объектами.



Модель состояний MIDлета



Модель состояний MID-лета

MID-леты переходят к различным состояниям в течение их жизненного цикла. Спецификация MIDP определяет модель перехода из режима в режим. В таблице 3.1 перечислены возможные состояния MID-лета и их соответствующие описания.



Программная cтpyктypa MIDлета



Программная cтpyктypa MID-лета

Теперь, когда.вы изучили жизненный цикл приложения, наступило время взглянуть на исходный код простого MID-лета. Вы, возможно, уже догадались, что я собираюсь начать с показа наипростейшего MID-лета - MIDP-версии все той же злополучной программы «HelloWorld». В листинге 3.1 показан исходный код для первой версии MID-лета HelloWorld.



Реализации MIDP создают только



Рисунок 3.6. Реализации MIDP создают только один объект Display на один MID-лет. Ваш MID-лет является примером вашего основного класса, который дополняет класс MID-лета. Однако он может создавать много объектов Displayable


Важными понятиями являются следующие:

Объект Display управляет физическим дисплеем. Display может отображать объекты Displayable. Вы должны получить ссылку на объект Display, связанный с вашим MID-летом реализацией MIDP. Только один объект Displayable может быть отображен единовременно.

Диаграмма наследования является прекрасным инструментом, способным помочь в систематизации понимания модели программирования и связей между классами. На рисунке 3.7 показана диаграмма наследования всех классов в пакете javax.microedition.lcdui.

В частности, отметьте равнородные связи между типами Display и Displayable, их цели различны, поэтому они не имеют связей наследования. Также отметьте, что Form, которую создала наша программа «Hello World», является видом Displayable, называемым Screen. По идее эта организация поддерживает понятие того, что Form может взять на себя роль screen верхнего уровня.

Screen также является абстрактным классом. Он инкапсулирует природу всех типов объектов верхнего уровня экрана в MIDP. Form является отдельным конкретным подклассом Screen, используемым MID-летом HelloWorld.

MID-лет HelloWorld добавляет String в Fora. Эта способность объединять объекты делает класс Form разновидностью контейнера. Хотя контейнеры являются основой моделей программирования AWT и Swing, MIDP не имеет на самом деле такого понятия. Класс Form является единственным типом MIDP, который способен содержать что-либо еще.

Формы могут содержать только три типа объектов: Strings, Images и Items. Form не может содержать другой Displayable любого рода, даже Screen или другой Form. Иерархия наследования, показанная на рисунке 3.7, подтверждает это. Это означает, что формы не могут быть представлены в форме вложений. Эта модель значительно упрощает структуру приложений MIDP по сравнению с графическим интерфейсом пользователя AWT или Swing. Поддержка вложенной структуры означала бы, что реализации пришлось бы поддерживать абстракцию визуального представления исполняемой иерархии вложенности для пользователя. Эта возможность была намеренно исключена из MIDP, потому что ресурсы, требуемые для поддержки родственных абстракций, слишком дороги для мобильных устройств.

Обратите внимание, что на рисунке 3.7 классы Item и Image не находятся под иерархией Displayable и поэтому не являются отображаемыми объектами. Items, Images и Strings могут быть добавлены в формы с помощью методов из класса Form, показанных в таблице 3.3.



Главное окно этого


Нажмите на красную кнопку с трубкой («hang up» - «отбой») на эмуляторе и вы вернетесь в главное окно AMS. Закрыв окно эмулятора, вы завершите его работу. Теперь вы закончили полный жизненный цикл выполнения приложения. Далее в этой главе вы узнаете больше о деталях жизненного цикла MID-лета и модели состояний MID-лета.

Свойства приложения



Свойства приложения

Вы узнали о наличии определенных атрибутов MID-лета, которые описываются в файле JAD каждого набора MID-летов. Вспомните, что всем MID-летам требуются атрибуты.



перечисляет требуемые



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

Когда AMS устанавливает набор MID-летов на устройстве, она размещает JAD-файл MID-лета в определенном месте под своим контролем. Когда MID-лет запускается, AMS считывает JAD-файл и строит структуру данных атрибутов приложения. MID-лет может считывать значение атрибута с помощью метода класса MIDlet

String getAppProperty(String key)

Параметр key является именем атрибута, например, MIDlet-Name. Возвращаемая строка является связанным значением, находящимся в файле JAD.

Листинг 3.3 демонстрирует, как MID-лет может извлекать атрибуты. Он модифицирует листинг 3.2, добавляя вызов к методу printAppProperties() в конце метода startApp(). Новый метод startApp() :



Состояния MIDлета



Таблица 3.1. Состояния MID-лета

Название состояния MID-лета

Описание

Paused (Приостановлен)

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

Active (Активен)

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

Destroyed (Прерван)

MID-лет не запущен и уже не может переходить в другие состояния.

На рисунке 5 показана диаграмма перехода из состояния в состояние, которая представляет из себя эти режимы MID-лета и события, которые служат причиной перехода из одного состояния в другое. Методы startApp(), pauseApp() и destroyApp(), которые вы видели в листинге 3.1, позволяют MID-лету изменять свое состояние. Технически программа управления приложениями устройства изменяет состояние MID-лета, вызывая один из этих методов в MID-лет. MID-лет не может сам изменять свое состояние, хотя он может запрашивать об изменении состояния AMS.

Программа управления приложениями сначала создает экземпляр класса вашего MID-лета, вызывая его конструктор no-argument. Затем она устанавливает его в приостановленное состояние. Прежде чем MID-лет сможет выполняться, AMS должна поместить MID-лет в активное состояние в первый раз. Она помещает MID-лет в активное состояние и затем вызывает метод MID-лета startApp ().



Методы классов MIDлетов



Таблица 3.2. Методы классов MID-летов, управляющие состояниями MID-летов

Название метода класса MID-лета

Описание

protected abstract void destroyAppf)

AMS сигнализирует MID-лету о прекращении работы. MID-лет входит в прерванное состояние

void notifyDestroyed ()

MID-лет запрашивает о входе в прерванное состояние

void notifyPausedf)

MID-лет запрашивает о дезактивации и входе в приостановленное состояние

protected abstract void pauseApp()

AMS сигнализирует MID-лету остановиться, MID-лет входит в приостановленное состояние

void resumeRequest ()

МЮ-лет запрашивает о повторном входе в активное состояние

protected abstract void startApp()

AMS сигнализирует MID-лету, что он активен

Обратите внимание на то, что программа в листинге 3.1 не вызывает System.exit(). Приложения MIDP отличаются от приложений J2SE тем, каким образом они заканчивают работу. Для завершения работы вашего MID-лета вы просто должны вызвать метод MID-лета notifyDestroyed(). Это сигнализирует AMS, что ваш MID-лет завершил выполнение. AMS закрывает MID-лет и все его объекты. Однако виртуальная машина продолжает работу.

Вы хотите, чтобы виртуальная машина продолжала работу, чтобы можно было запустить другие MID-леты. Вызов System.exit() сигнализирует виртуальной машине завершить свою работу. Такое поведение нежелательно в приложениях MIDP. Ваши приложения не должны завершать работу виртуальной машины, в действительности они и не могут этого сделать. Если ваше приложение вызывает System.exit(), java.lang.SecurityException обязательно прекратит работу. Вы увидите что-либо сходное со следующим:

java.lang.SecurityException: MIDP lifecycle does not support system exit.
(Жизненный цикл MIDP не поддерживает системный выход).
at Java.lang.Runtime.exit(+9)
at Java.lang.System.exit(+7)
at HelloWorld3$MyCommandListener.commandAction(+15)
at javax.microedition.Icdui.Display$DisplayAccessor.
commandAction(+99)
at сот.sun.kvem.midp.Icdui.EmulEventHandler$EventLoop.run(+430)

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



Методы класса формы



Таблица 3.3. Методы класса формы для добавления элементов в объект Form

Название метода класса формы

Описание

public int append (Item item)

К данной форме добавляется объект Item

public int append (String string)

К данной форме добавляется объект String

public int append (Image image)

К данной форме добавляется объект Image

Класс Form реализует абстракции, необходимые для отображения объектов String, Image и Item. Он также отвечает за реализацию политики организации объектов, которые были к нему добавлены. Другими словами, реализация Form определяет политику расположения.

Тем не менее, в MIDP нет понятия диспетчеров расположения, которыми мог манипулировать программист на AWT или Swing. Спецификация MIDP рекомендует, чтобы реализации Form сопровождались компоновкой, но она не устанавливает обязательных правил. Реализации могут варьироваться в способе, которым они осуществляют компоновку формы.

Иерархия Item определяет визуальные компоненты. Вы должны, однако, различать эти компоненты, которые имеют визуальное представление и отображаемые компоненты, которые являются компонентами высшего уровня. Отражены могут быть конкретные подклассы Itern. Однако они не могут быть отображены независимо как компоненты высшего уровня Screen. Более того, они могут быть отображены только с помощью объекта Fo rm, но не другого типа Screen.



Стандартные системные свойства CLDC



Таблица 3.4. Стандартные системные свойства CLDC

Ключ свойства

Описание

Значение по умолчанию

mi с г oedit ion. con figuration

Название и версия поддерживаемой конфигурации

CLDO1.0

microedit ion. encoding

Набор знаков кодировки по умолчанию, используемый платформой

IS08859-1

micr oedit ion. locale

Название текущей местной среды платформы

нуль

microedition. platform

Название платформы или устройства

нуль

micr oedition. profiles

Названия всех поддерживаемых профилей

нуль

Листинг 3.2 иллюстрирует отслеживание системных свойств в MID-лете. Код дополняет пример, указанный в листинге 3.1.



В этой главе вы узнали



Выводы по главе

В этой главе вы узнали о базовой организации и структуре приложений MIDP. Центром модели программирования MIDP является MID-лет.
Основными компонентами структуры MID-лета являются экземпляр MID-лета, экземпляр Display и одна или более «штучек» Displayable, которые являются компонентами пользовательского интерфейса.
Объекты MID-лета связаны с объектом Display. MID-леты создают элементы Displayable, которые являются компонентами пользовательского интерфейса, и требует, чтобы они отображались на экране устройства. Display управляет экраном устройства и видимостью элементов пользовательского интерфейса.
Абстрактный класс Screen является первым из двух основных типов, которые катего-ризируют все объекты Displayable. Класс Screen является центральной абстракцией дисплея. Класс Form является конкретным подклассом Screen. Только Screen видим в каждый момент жизни MID-лета.
Все MID-леты имеют связанные с ним свойства и атрибуты. Свойства являются стандартными системными свойствами, определенными спецификацией CLDC. Они относятся к платформе и поддерживаются и управляются системой управления приложениями. Атрибуты связаны с MID-летами. Существуют обязательные атрибуты, которые доступны для всех MID-летов, и необязательные атрибуты. Кроме того, существуют определяемые в зависимости от приложения атрибуты, которые могут быть определены автором MID-лета. Атрибуты существуют в файле JAD приложения и управляются программным обеспечением AMS устройства во время выполнения.

Жизненный цикл выполнения приложения



Жизненный цикл выполнения приложения

Здесь приведен пример этапов, включаемых в выполнение приложения:

Запуск эмулятора. Вы увидите появившееся окно, которое имитирует интерфейс устройства. Если вы используете J2MEWTK версии 1.0.2, вы заметите, что эмулятор просто выполняет приложение HelloWorld, потому что это единственное приложение, присутствующее в наборе. На рисунке 3.1 показано главное окно выполнения этого MID-лета. Однако, если у вас J2MEWTK версии 1.0.3, вы увидите список выбора из всех MID-летов, даже если он у вас один. Добавьте вторую версию программы, названную HelloWorld2, к набору MID-летов. Вы можете начать этот процесс, нажав на кнопку Settings... (Параметры...) на основном окне инструментария, которое вы видели на рисунке 2.5. Во-первых, напишите исходный код и затем поместите его в директорию проекта srс /. Добавьте его к набору MID-летов, выбрав закладку MIDlets (MID-леты) в окне параметров проекта. На рисунке 3.2 показано окно конфигурации после добавления нового MID-лета.



Добавление нового MIDлета к набору



Рисунок 4.3. Добавление нового MID-лета к набору приводит в резул: тате к тому, что AMS отображает меню, из которого вы выбираете приложение, которое хотите запустить




Эта диаграмма объекта показывает



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


В отличие от инструментария Swing MIDP не имеет общей модели блока прослушивания событий. Высокоуровневый API имеет только один тип командного блока прослушивания, называемого, что неудивительно, javax.microedition.lcdui.Command-Listener.

В листинге 4.1 показана вторая версия MID-лета HelloWorld. Она добавляет экранные клавиши к главному окну и устанавливает блок прослушивания команд для распознавания нажатия пользователем на экранную клавишу. MID-лет отвечает, показывая другой вид экрана, называемый уведомлением (alert), который является MIDP-эквивалентом всплывающего диалогового окна.

Листинг 4.1. В программу HelloWorld2 добавлена обработка команд

import javax.microedition.midlet.MIDleC;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.ledui.Displayable;
import javax.microedition.lcdui.Form;
* /**
Вторая версия приложения HellcWorld.
Эта версия добавляет команду в компонент Displayable и устанавливает
* /
• блок прослушивания команд для активации команд и принятия какого-либо
действия в ответ на нее. Этот пример демонстрирует, как Displayable
определяет семантику выполнения команд.
*/
public class HelloWorld2 extends M.I Diet

// Display. Этот Объект управляет всеми компонентами Displayable.
private Display display;
// Displayable. Этот компонент отображается на экране, private-Form form;
private final String ALERT_LABEL = "Alert Me!"; private Alert alert;
// Две команды добавлены к отображаемым на экране объектам
// этого MID-лета. private Command showAlert; private Command sayHi;
// Экземпляр внутреннего класса, который определяет
// CommandListener для этого MID-лета.
private MyCommandListener cl = new MyCommandListener();
public HelloWorld2()
(
super();
public void destroyApp(boolean destro()
form = null; notifyDestroyed();
}
public void pauseApp()
)
public void startApp()
form = new Form("Hello World();
String msg = "My second MIDletl"; form.appendjmsg);
form.setCommandListener(cl) ;
showAlert = new Command(ALERT_LABEL, Command.SCREEN, 1);
form.addCommand(showAlert) ;
sayHi = new Command("Say Hi", Command.SCREEN, 1);
form.addCommand(sayHi);
display = Display.getDisplay(this) ; display.setCurrent(form);
}
private class MyCommandListener implements CommandListener
public void commandAction(Command c, Displayable d)
{
'alert = new Alert("Button pressed",
"The '" + ALERT_LABEL + "' button was pressed", null, AlertType.INFO); alert.setTimeout(Alert.FOREVER); display.setCurrent(alert, form);
}
}
}

Я создал вышеописанный МID-лет HelloWorld2 с помощью моего любимого текстового редактора. Затем я разместил исходный файл под управление установленного у меня инструментария Wireless Toolkit:

? pwd
/cygdrive/c/J2mewtk/apps/HelloWorld/src
$Is
HelloWorld.lava HelioWorld2.Java
$

J2ME Wireless Toolkit компилирует все файлы .Java в директории. Компиляция проекта HelloWorld компилирует обе версии HelloWorld. На рисунке 4.3 показан стартовый экран эмулятора, который появляется, когда я открываю проект HelloWorld. Обратите внимание, что теперь вы видите два MID-лета в списке. Используя клавиши стрелок, выберите HelloWorld2 и запустите его, нажав экранную кнопку Launch (Запуск).

На рисунке 4.4 показан главный экран HelloWorld2. Обратите внимание, что теперь справа появилась экранная клавиша под названием «Alert Me». На этом устройстве недостаточно места, чтобы показать полное сообщение «Alert Me!», которое установлено в исходном коде - так что восклицательный знак пропущен.

Это первая проблема транспортабельности, с которой вы столкнулись, и она имеет практическое значение. К счастью, эмулятор поддерживает четыре различных устройства. Вы можете выполнить ваши MID-леты с помощью любого из четырех эмуляторов устройств, поддерживаемых эмулятором J2ME Wireless Toolkit Emulator, чтобы посмотреть, как они выглядят на каждом. Таким образом вы можете обнаружить множество потенциальных проблем, связанных с транспортабельностью.



Эта диаграмма UML показывает связь



Рисунок 4.1. Эта диаграмма UML показывает связь между несколькими ключевыми классами, которые ответственны за создание, обнаружение и передачу командных событий вашему приложению


Обратите внимание, что эта диаграмма не является всесторонним UML-представлением каждого представителя, атрибута типов и так далее. На рисунке 4.2 показана диаграмма экземпляра объекта, которая отражает взаимодействие экземпляров этих классов в работающем приложении.



Нажатие на экранную клавишу «Alert



Экранная навигация

Пример HelloWorld2 демонстрирует центральную абстракцию MIDP - Screen. Вы, несомненно, уже обратили внимание, что.можете отображать одновременно только один Displayable - один Screen. Когда приложению необходимо отобразить Alert, ему приходится заменять основной экран.

Причина этой одноэкранной абстракции кроется в ограниченности экранных ресурсов устройства. В отличие от инструментов графических пользовательских интерфейсов для настольных компьютеров, таких, как J2SE Swing toolkit, вы не можете иметь большое количество накладываемых друг на друга окон, всплывающих уведомлений, диалоговых окон и так далее. Хотя внедрение этих абстракций и не невозможно, требования памяти и ЦП на современных мобильных устройствах таковы, что фактически запрещают это. Более того, жидкокристаллические дисплеи с низким разрешением, низким потреблением энергии и маленькой площадью, характерные для большинства мобильных устройств, не приспособлены для этих абстракций. Даже дисплеи «карманных компьютеров» имеют минимально приемлемые характеристики для наложения окон, всплывающих окон и тому подобного. Однако очень вероятно, что примерно через год эти устройства будут иметь значительно большие мощности, порядка 50 МГц и 32 Мб RAM.

Существует простая идиома экранной навигации, связанная с этой абстракцией экранов дисплея. Если вы хотите отобразить новый экран, просто установите, что экран является отображаемым в настоящий момент (current displayable). Чтобы сделать это, вы запрашиваете.объект Display вашего MID-лета на отображение Screen. Вспомните, в главе 2 вы узнали, что каждому MID-лету при запуске присваивается уникальный объект Display реализацией MIDP. Вы никогда не создаете объект Display, но вы можете получить ссылку на него, сделав следующий вызов со ссылкой на ваш MID-лет как на аргумент: Display.getDisplay(midlet);

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

display.setCurrent(nextDisplayable)

Вы можете найти эти две строчки кода в методе startAppf) обеих версий приложения HelloWorld.

Разработка навигации и перемещений в вашем МЮ-лете включает следующие этапы:

Создание экранов. Создание команд, которые вам нужны для каждого экрана. Присвоение команд экранам.

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

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



Oбpaбoткa кoмaнд



Oбpaбoткa кoмaнд

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

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

Класс Command в пакете javax.microedition.lcdui описывает команды. Этот класс инкапсулирует информацию о:

метке (label); приоритетности (priority); типе команды (command type).

Метка - это String, подходящая для дисплея, с условием, что она может предоставлять пользователю семантику команды. Приоритетность является int, которая отражает важность команды по отношению к другим командам. Тип команды - это внутреннее представление намеченного использования команды. Текущая спецификация определяет типы команды, перечисленные в таблице 4.1.



Организация команд



Организация команд

Взглянув более внимательно на пример, приведенный в предыдущем разделе, вы можете догадаться, что на самом деле вы не можете контролировать то, где появляется каждая из меток Command на экранных клавишах. Как-никак, я не указывал левую или правую экранную клавишу для размещения обеих Command. Приложение HelloWorld2 добавило клавиши «Alert Me!» и «Say Hi», в таком порядке. Первая появилась на правой экранной клавише, вторая - на левой.

В действительности реализация управляет размещением меток Command на ваших отображаемых объектах Displayable в соответствии с некоторой политикой, зависящей от реализации. Вы можете познакомиться с различными политиками, просмотрев различные эмуляторы в беспроводном инструментарии. Вы сможете увидеть, что эмулятор Motorola размещает клавиши не так, как эмуляторы стандартных черно-белого и цветного телефонов.

Следующая версия нашей программы HelloWorld, HelloWorldS, добавляет третью команду к главному экрану. Вместо того чтобы приводить вновь весь MID-лет целиком, я просто покажу части, которые отличаются от предыдущего примера.

В масштабах класса HelloWorld3 определяет три объекта Command:

/**
Третья версия приложения HelloWorld.
Эта версия встраивается поверх HelloWorld2 с помощью добавления
нескольких команд к компоненту Displayable. Здесь демонстрируется,
что ComraandListener должен определять, какая из команд была
активирована на экране.
Вы также можете видеть, как реализация расставляет команды
на экранных клавишах и как она создает меню и упорядочивает команды
в меню в соответствии с типом команды.
*/
public class HelloWorld3 extends MIDlet,
{
Command showAlert= new Command("Alert Me!", Command.SCREEN, 1);;
Command sayHi = new Command("Say Hi", Command. SCREEN, I);-;
Command cancel = new Command("Cancel", Command.CANCEL, 1);
public HelloWorld3()
{
super ();
}
. . .
}

В методе startApp() эти объекты Command добавляются к главному экрану следующим образом:

form.addComraand(showAlert) ;
form.addCommand(sayHi) ;
form.addCommand(cancel);

Создание и запуск этой новой версии в эмуляторе J2ME Wireless Toolkit Emulator отражен в главном экране, показанном на рисунке 4.7.

Во-первых, обратите внимание, посмотрев на рисунок 4.7, что вы видите метку «Меню» на правой экранной клавише при запуске этого последнего MID-лета с помощью эмулятора стандартного черно-белого телефона. В программном коде определенно нигде нет определения меню.

Устройства имеют только две экранные клавиши, но мы вставили три команды в наш главный экран. Реализация обнаружила это и создала меню, которое содержит вторую, третью и другие команды. На рисунке 4.8 показан дисплей после того, как вы выбрали клавишу «Меню».

Запустите эту последнюю немодифицированную версию с помощью эмулятора Motorola iS5s, и вы увидите, что ключ «Меню» появится на левой экранной клавише, что отражено на рисунке 4.9. В действительности рисунки 4.8 и 4.9 демонстрируют, что конкретное поведение и политика размещения меню зависят от реализации.



показан основной экран HelloWorld2, как



Рисунок 4.4. Основной экран MID-лета HelloWorld2


Ha рисунке 4.5 показан основной экран HelloWorld2, как он появляется при имитировании устройства Motorola i85s. Обратите внимание, что в отличие телефона со стандартными настройками, он способен отображать полный текст команды на экранной клавише. Есть также вторая Command с меткой «Say Hi», которая появляется на экранной клавише слева.

Нажатие на экранную клавишу «Alert Me!» отображает экран уведомления, показанный на рисунке 4.6. Действие отображения этого экрана является поведением, которое HelloWorld2 определило для команды, связанной с выбором экранной клавиши.

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

Вам не придется делать это таким способом. Вы можете, например, создать подкласс класса Form и заставить его реализовать CommandListener. Ваш подкласс является Form, который является разновидностью Screen, и поэтому он способен получать уведомления о вызове команды.

Размещение меток — команд зависит



Упорядочивание команд

Вы, должно быть, удивлены, почему команда «Cancel» (Отмена) была помещена на экранную клавишу, даже несмотря на то, что бна была добавлена на экран последней. Интуитивно вы можете предположить, что она должна добавляться последней в меню. Вы бы предположили, конечно, что клавиша «Alert Me!», которая была добавлена первой, должна быть на экранной клавише.

Объяснение этого очевидного отклонения заключается в том, что команды организуются в соответствии с их типом. Вспомните из предыдущего раздела этой главы, что одной из трех частей информации, которые определяют Command, является тип команды. Класс Command определяет константы, которые представляют собой действующие типы. Вы видели перечисление этих констант в таблице 4.1.

Теперь я добавляю следующие объекты Command в пример HelloWorld3. На уровне классов я определяю следующие новые команды:

...
public class HelloWorid3 extends MIDlet
private Command exit = new Command("Exit", Command.EXIT, 2);
private Command help = new Command ("Help", Command.HELP, 3);
private Command item. = new Command ("Item", Command . ITEM, 4 ) ;
private Command ok = new Command("OK", Command.OK, 5);
private Command screen = new Command("Screen", Command.SCREEN, 6);
private Command stop = new Command("Stop", Command.STOP, 7);
...
}

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

В методе startApp() я добавляю эти новые объекты команд к главному экрану. Новая версия startApp() выглядит таким образом:

public void startApp()
// Создайте элемент Displayable. form = new Form("Hello World");
// Добавьте элемент строки в форму. String msg = "My first MIDlet!"; form.append(msg);
// Добавьте MyCommandListener в Form, чтобы принимать
// события нажатия клавиш, которые должны порождать
// всплывание диалогового окна уведомления, form.setCommandListener(cl);
form.addCommand(showAlert); form.addCommand(sayHi);
form.addCommand(cancel) ;
form.addCommand(exit} ;
form.addCommand(help); form.addCommand(item);
form.addCommand(ok); form.addCommand(screen);
form.addCommand(stop);
// Это приложение просто отображает одну форму, созданную выше,
display = Display.getDisplay(this); display.setCurrentfform);
}

Когда вы запустите новую версию, первое, что вы должны заметить, это то, что команда «Cancel» («Отмена») замещена командой «Exit» («Выход») на экранной клавише, как показано на рисунке 4.10. Активация меню показывает, что клавиша «Cancel» («Отмена») все еще на самом деле здесь, но теперь она в меню.



Реализация MIDP определяет политику



Рисунок 4.10. Реализация MIDP определяет политику размещения команд в соответствии с их типом


Размещение команд осуществляется в соответствии с их типом. Конкретная же политика, однако, зависит от реализации.



Сценарий oбработки команд



Сценарий oбработки команд

Сценарий обработки команд в MIDP является теоретически сходным с другими ин-струментариями графического пользовательского интерфейса. Блок прослушивания команд (command listener) является объектом, который получает уведомления о наличии команд. Блоки прослушивания команд регистрируются для получения уведомления о командах.

Некоторые внешние действия, такие, как нажатие пользователем на кнопку, отражаются в реализации MIDP, обнаруживая событие и связывая его с отображаемым в настоящее время экраном. Это инкапсулирует событие в объект Command. Зарегистрированный блок прослушивания команд получает уведомление о событии. Блок прослушивания затем предпринимает какое-либо действие, которое отражает поведение команды.

Команды могут быть связаны только с элементами Displayable. To есть вы можете добавлять или удалять объекты Command в и из объекта Displayable с помощью следующих методов класса Displayable:

public void addCommand(Command crad)
public void removeCoramand(Command cmd)

Объект блока прослушивания команд должен прикрепляться к Displayable для получения уведомления о команде с помощью вызова следующего метода в объекте

Displayable:
void setCommandListener(CommandListener cl)

Только один блок прослушивания команд разрешен на один Displayable. Реализация MIDP поставляет команды только в текущий Displayable. Это ограничение пришлось ввести с учетом реалистичных ожиданий от производительности современных реализаций MIDP. MIDP определяет модель одной нити для обработки событий. Поддержка многочисленных блоков прослушивания команд потребовала бы модели со множеством нитей обработки событий.

На рисунке 4.1 показана диаграмма UML связей между классами Displayable и Command и интерфейсом CommandListener.



Семантика команд



Семантика команд

Взгляните вновь на команду «Exit» («Выход»). Объект Command определяется с помощью метки «Exit» («Выход»). Но это не делает команду командой выхода! Я указал тип Command.EXIT в вызове конструктора. Это указание атрибута типа делает команду командой «Exit» («Выход»). Если бы я задал ее тип, как, скажем, Command. SCREEN, эта команда не появилась бы на экранной клавише. Вы можете попробовать проделать это самостоятельно.

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

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

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

В действительности расстановка приоритетов команд не является столь важной, когда вы работаете с высокоуровневыми API MIDP. Тем не менее, важно знать об этом понятии. Обычно пользователь не способен делать только одну вещь за раз, так что не будет лишним добавить высокоприоритетные события в приложение.



Типы команд


Константа типа команды

Описание

public static. int BACK

Возврат к логически предыдущему экрану

public static int CANCEL

Стандартный отрицательный ответ на запрос в диалоге

public static int EXIT

Указание на выход из приложения

public static int HELP

Запрос помощи в онлайновом режиме

public static int ITEM

Подсказки приложения для реализации, к которой команды имеют отношение, по определенному элементу на экране, возможно, по выбранному в настоящее время элементу

public static int OK

Стандартный положительный ответ на запрос в диалоге

public static int SCREEN

Программно определяемая команда, имеющая отношение к отображаемому в настоящее время экрану

public static int STOP

Остановка некоторой выполняемой в настоящее время операции



В этой главе вы узнали



Выводы по главе

В этой главе вы узнали о высокоуровневом программном интерфейсе приложения (API) MIDP. Абстракции высокоуровневого API описывают следующее:
визуализация элементов пользовательского интерфейса; обработка событий. MID-леты, которые используют высокоуровневый API, не могут изменять внешний вид и впечатление от элементов, И они не могут получать информацию о реальных клавишах устройства и нажимаемых кнопках, которые служат причиной активизации команды.
Команды получают только семантическую информацию о «событии». Команда не представляет собой поведение или действие, которое осуществляется в ответ на событие. Блоки прослушивания команд задают поведение команд, определяя обработку, которая осуществляется в результате запуска команды реализацией.
Определенная политика размещения меток команд на экране зависит от реализации. MIDP устанавливает, что размещение команд в меню должно быть сделано в соответствии с типом команды.
Приоритетность команд диктуется порядком, в котором команды запускаются и посылаются в блок прослушивания команд.

DateField



DateField

На главном экране демонстрационного приложения UlComponent (смотри http://www.phptr.com/) вторым элементом списка является демонстрационная версия класса DateField. На рисунке 5.1 показано, что DateField является разновидностью Item; как таковой, он должен быть частью Form для того, чтобы быть отображаемым. В листинге 5.5 показан исходный код файла DateFieldDemo.java.



Другие экранные типы



Другие экранные типы

Вы видели все компоненты MIDP за исключением одного: TextBox. В отличие от TextField TextBox является многострочной редактируемой текстовой областью. Взгляните еще раз на наглядную иерархию наследования, показанную на рисунке 5.1, и вы увидите, что TextBox является видом Screen, а не Item.

Поскольку TextBox является Displayable, вы должны создать объект MID-лета для демонстрации его использования, вы не можете разместить его в другом Screen или Form, как вы могли поступить с компонентами, происходящими от Item. На рисунке 5.11 показан экран TextBoxDemo.



Другие компоненты Item



Другие компоненты Item

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



Платформа программирования J2ME для портативных устройств



Gauge

Класс Gauge также является производным от Item. Запуск GaugeDemo из основного экрана создает дисплей, показанный на рисунке 5.8.



Главный экран демонстрационной



Листинг 5.2. Уведомления являются экранами, но они не могут содержать объекты Command. Вы должны указать Displayable, который должен быть показан, когда уведомление будет недоступно

import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.Icdui.TextField;
/**
Демонстрирует использование объектов Alert.
*/
public class AlertDemo extends Form implements CommandListener
{
private Command go = new Command("Go", Command.SCREEN, 1);
private Command back = new Command ("Back", Command.BACK, 1);
private ChoiceGroup type; private TextField tPref;
private String [] elements =
{
"Alarm", "Confirmation", "Error", "Information", "Warning" );
// Это необходимо/ чтобы другие экраны могли ссылаться
// на экземпляр этого класса, private static Displayable instance;
/**
Конструктор.
*/
public AlertDemo()
{
'super ("Build alert");
type = buildAlertTypeSelection ();
tPref = buildTimeoutPrefPrompt();
append(type); appendftPref) ;
addCommand(go); addCommand(back);
setCommandListener(this) ; instance = this;
}
/**
Возвращает единственный экземпляр этого класса.
Вызов этого метода перед созданием объекта возвращает Пустой указатель.

@возвращает экземпляр этого класса.
*/
static Displayable getlnstance ()
{
return instance;
}
private ChoiceGroup buildAlertTypeSelection ()
{
// He работает, если это Choice.IMPLICIT. Смотри документацию Choice.
// Тип IMPLICIT действителен только для объектов List,
return new ChoiceGroup ("Alert Type", Choice.EXCLUSIVE, elements, null);
}
private TextField buildTimeo-utPref Prompt ()
}
String MAX_TIMEOUT_VALUE = "5"; int MAX_SIZE = 8;
return new TextField("Timeout (sec.)", MAX_TIMEOUT_VALUE,
MAX_SIZE, TextField.NUMERIC);
}
public void comraandAction(Command c, Displayable d)
{
UIComponentDemo demo = UIComponentDemo.getlnstance();
Display display = Display.getDisplay(demo); int timeSec; int timeMillis;
if (c == go)
// Уведомления не принимают определенные приложением команды.
String title = elements[type.getSelectedlndex()]; 1;
Alert alert = new Alert (title) ;
alert.setString("A '" + title + "' alert"); timeSec = Integer . parselnt(tPref.getString());
timeMillis = timeSec * 1000; if (timeMillis <= 0)
(
timeMillis = Alert.FOREVER;
}
alert.setTimeout(timeMillis);
display.setCurrent(alert, AlertDemo.getlnstance());
}
if (c == back)
(
UIComponentDemo.getlnstance().display ();
}
)
}

Когда вы будете экспериментировать с этим приложением, обратите внимание, что вы можете прокрутить List вверх и вниз, выделяя различные элементы List, но программного выбора событий не осуществляется. Подобным образом на экране Build Alert (Создание уведомления) вы можете прокручивать и многократно выбирать элементы ChoiceGroup без активации какого-либо действия.

В обоих случаях событий не генерируется, пока вы не вызовете активацию команды. На экране List вы должны нажать на кнопку выбора Select, чтобы перейти к экрану Build Alert (Создать уведомление). Когда вы окажетесь на экране Build Alert (Создать уведомление), вы должны выбрать экранную кнопку Go, чтобы просмотреть отображенный Alert. Изменение выбора в любой реализации Choice не активирует какую-либо Command в компоненте.

Оба экрана, изображенные на рисунках 5.2 и 5.3, показывают наборы элементов, из которых пользователь может сделать выбор. Оба компонента List и ChoiceGroup реализуют интерфейс javax.microedition.ldcui.Choice, который указывает характеристики поведения компонентов, поддерживающих выбор одного или более своих элементов. Интерфейс Choice определяет три константы:

IMPLICIT (Неявный): выбирается элемент, на котором в настоящее время сфокусировано внимание. EXCLUSIVE (Исключающий): может быть выбран один-единственный элемент. MULTIPLE (Множественный): могут быть выбраны несколько элементов.

Только объекты List могут устанавливать активацию IMPLICIT. Когда вы активизируете клавишу выбора Select устройства при неявном List, какой бы элемент List и был бы выделен в данный момент, он будет выбран. Листинг 5.1 демонстрирует эту неявную команду. ChoiceGroup не может быть неявным. Конструктор ChoiceGroup сбрасывает IllegalArgumentException, если вы пытаетесь создать его экземпляр с типом Choice.IMPLICIT.

Есть еще один тип информации, которая может быть собрана из этого неявного List. Ранее я говорил, что событие команды посылается в Displayable в ответ на нажатие пользователем кнопки Select на устройстве. Тип этой команды, однако, отличается от любого из типов, которые определяет класс Command.

Класс List определяет особый объект Command, List.SELECT_COMMAND. Активация списка IMPLICIT генерирует эту особую команду и посылает ее в блок прослушивания команд без какой-либо явной операции выбора, осуществляемой пользователем. Истинная цель этой команды заключается в том, чтобы дать возможность методу блока прослушивания commandAction() распознать активацию операции выбора устройства. В листинге 5.3 показано, как метод UIComponentDemo.commandAction() использует эту специальную константу.



Иерархия Компонентов пользовательского интерфейса MIDP



Иерархия Компонентов пользовательского интерфейса MIDP

Диаграмма иерархии наследования MIDP, показанная на рисунке 5.1, повторяет то, что вы уже видели на рисунке 3.7 в главе 3. Вы уже видели некоторые из компонентов пользовательского интерфейса MIDP, показанные в этой иерархии, а именно Displayable, Screen, Form и Alert.

Вы знаете, что класс Displayable определяет природу основы любого компонента, который может быть отображен, и что класс Screen определяет базовую абстракцию пользовательского интерфейса MIDP - экран. Класс Screen является первым Displayable, который вы видели, a Form был первым конкретным типом используемого экрана.

В таблице 5.1 кратко описаны все компоненты пользовательского интерфейса MIDP в пакете javax.micfoedition.lcdui.



Imageltem



Imageltem

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



элемента TextBox пользовательского интерфейса MIDP.



Листинг 5.10. Текстовые окна являются экранами и не нуждаются в форме, в которой можно существовать

import jav,ax.micro etiition.lcdui. Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.TextBox;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
/**
Этот MID-лет демонстрирует использование отображаемого
элемента TextBox пользовательского интерфейса MIDP.
Усмотри javax.microedition.Icdui.TextBox
* /
public class TextBoxDemo extends MIDlet implements CommandListener
private Command quit = new Command("Exit", Command.EXIT, 1);
private static TextBoxDemo instance;
// Компонент пользовательского интерфейса TextBox. private TextBox textBox;
// Максимальное число знаков, которое TextBox может
// поддерживать. private int MAX_SIZE = 100;
// Первоначальный текст в TextBox. private String initialText =
"You can edit the contents of this TextBox";
/**
Конструктор.
*/
public TextBoxDemo()
super () ; instance = this;
}
public void pauseApp()
{
. . .
}
public void destroyApp(boolean destroy)
}
textBox = null; initialText = null; instance = null;
}
void quit()
}
destroyApp (true);
notifyDestroyedf);

public void startApp()
{
texcBox = new TextBoxC'A TextBox", initialText, MAX_SIZE,
TextField.ANY); сextBox.addCommand(quit); textBox.setCommandListener(this);
display();
}
/**
Возвращает единственный экземпляр этого класса.
Вызов этого метода до создания объекта возвратит пустой указатель.
@возращает экземпляр класса.
*/
public static TextBoxDemo getlnstance()
return instance;
}
public void display!)
{
Display. getDisplay(this).setCurrent(textBox);
}
public void commandAction(Command c, Displayable d)
if (c == quit)
{
quit();
}
}
}

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

На рисунке 5.11 изображен первоначальный текст, используемый для создания экземпляра TextBox. Как и в случаях с другими редактируемыми объектами, вы просто выбираете TextBox с помощью кнопки выбора Select эмулятора и затем редактируете содержимое. Вы можете переходить с помощью четырех клавиш стрелок, стирать знаки с помощью клавиши Clear (Очистить) и вводить их с помощью кнопочной панели, либо компьютерной клавиатуры при использовании эмулятора. Конечно, программа может также манипулировать содержимым с помощью API, который поддерживает вставку, удаление, установку максимального размера, установку ограничений и так далее. На рисунке 5.12 показан экран после выбора текстового окна для редактирования.

Экранная навигация



Экранная навигация

На данный момент вы познакомились со следующими компонентами пользовательского интерфейса:

MIDlet; Display; Displayable; Form; List; Alert; ChoiceGroup; TextField.

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

Однако такое поведение в MIDP не является автоматическим. Только один Displayable видим в любой момент времени и реализация не отслеживает какой-либо информации об отображаемых экранах.

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

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

projected static Displayable instance;

Это объявление имеет следующий сопутствующий метод:

public static Displayable getlnstance()
{
return instance;
}

Данйый метод объявляется с модификатором static, так что на него можно легко создать Ссылку из любого места в приложении без создания экземпляра класса, содержащего этот метод - таким образом можно избежать неправильного цикла работы приложения.

Приложение AlertDemo предоставляет экранную клавишу Back (Назад) на экране Build Alert (Создание уведомления), показанном на рисунке 5.3. Если вы нажмете эту клавишу, вы вернетесь обратно в главное окно. Посмотрите вновь на метод commandActionO данной программы, которая показана в листинге 5.4.



Экраны и экранные элементы



Экраны и экранные элементы

Первый пример в этой главе показывает вам основную разницу между двумя типами компонентов пользовательского интерфейса MIDP: компонентами Displayable и компонентами Item. Иерархия наследования, изображенная на рисунке 5.1, ясно отображает Эти две категории. Иерархия Displayable заключает в себе экраны, которые вы отображаете. Иерархия Item классифицирует элементы, которые могут быть собраны в один экран. Следующие примеры демонстрируют использование различных компонентов пользовательского интерфейса MIDP. Мы объясняем их использование по мере ознакомления с каждым.

В листинге 5.1 показан файл под названием UIComponentDemo.java, который определяет исходный код новой программы, демонстрирующий использование элементов MIDP. Этот файл использует код в других файлах, которые вместе составляют полную демонстрационную программу компонента пользовательского интерфейса.



Конкретный интерфейс предоставляемый



Рисунок 5.12. Конкретный интерфейс, предоставляемый для редактирования текстового окна, зависит от реализации




Исходный код UlComponentDemo



Листинг 5.1. Исходный код UlComponentDemo

import javax.raicroedition.midlet.MIDlet;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui .CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.Icdui.List;
/**
Демонстрируется использование высокоуровневых компонентов
пользовательского интерфейса MIDP. Этот демонстрационный класс создает
список демонстрационных программ для выбора пользователем. Элементы
в списке являются на самом деле именами первичных классов
демонстрационных программ. MID-лет создает экземпляр класса,
представленного среди элементов списка, выбираемого пользователем
и затем выполняемого им.
*/
public class UlComponentDemo extends MIDlet
implements CommandListener
private Command exit = new Command("Exit", Command.EXIT, 1);

// Имена различных демонстрационных программ: Элементы в этом списке
// являются именами первичных .классов для каждой демонстрационной
// программы, private static String [] demos =
"AlertDemo",
"DateFieldDemo",
"GaugeDemo",
"StringltemDemo", "TickerDemo",
"ImageltemDemo"
}i;
private static UIComponentDemo instance = null;
// Реальный компонент List, который отображает элементы,
// перечисленные в списке «demos» выше.
private List mainMenu = new List ("Select demo", Choice.IMPLICIT,
demos, null) ;
// Конструктор No-arg. public UIComponentDemo()
// Обратите внимание на вызов super(). Он выполняет
// конструктор no-arg в классе MID-лета. super () ;
instance = this;
}
/**
Возвращает один экземпляр этого класса. Вызов этого метода перед
созданием объекта возвратит пустой указатель.
^возвращает экземпляр этого класса.
*/
public static UIComponentDemo getlnstance()
{
return instance;
{
public void startApp()
{
Display display;
mainMenu.addCommand(exit);

mainMenu.setCommandListener(this) ;
*
display = Display.getDisplay(this);

display.setCurrent(mainMenu) ;
public void pauseAppf)
{
}
void quit() ,;
destroyApp(true);
notifyDestroyed();

)
public void destroyApp(boolean destroy)
(
}
public void display!)
}
Display.getDisplay(this).setCurrent(mainMenu);

}
public void commandAction(Command c, Displayabie d)
{
Displayabie displayable = null;
if (c == List.SELECT_COMMAND)
{
int index = mainKenu.getSeiectedlndex();
try
{
displayable = (Displayable)
Class.forName(demos[index]).new!nstance();

if (displayable == null)
}
return;
}
Display display = Display.getDisplay(this);

display.setCurrent(displayable);

}
catch (Exception e)
{
System.out.println("Got exception here!!!");

e.printStackTrace() ;
return;
}
}
else if (c == exit) 1 quit() ;
}
}
}

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

В листинге 5.1 определяется MID-лет. Его высокоуровневый экран является компонентом List, который отображает список вариантов, отражающих различные элементы, которые демонстрирует программа. На рисунке 5.2 показан список верхнего уровня демонстрационных приложений, которые вы можете запустить. Этот основной экран является экземпляром List.

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

List является видом Screen, который, конечно, является Displayable, и приспосабливается к знакомой ему общей структуре приложения. Вы можете видеть в листинге 5.1, что экземпляр List является отображаемым в настоящее время, по существу это объект, который получает события вызова команды. Сам MID-лет является блоком прослушивания таких событий, регистрируя себя как CommandListener для этих событий. Он реализует интерфейс CommandListener, а также определяет метод commandAction () .

Альтернативным способом создания блоков прослушивания является создание самого компонента блока прослушивания событий, которые в нем происходят. Чтобы выполнить это, однако, вам бы пришлось создать в классе компонента подклассы, в данном случае создав подкласс класса List. Я выбрал первый подход и использую стандартный класс List без создания подклассов.

На рисунке 5.2 изображен список демонстрационных программ компонентов пользовательского интерфейса. Имена, которые вы видите, являются именами основных классов для каждой демонстрационной программы. При выборе одного из них выполняется соответствующая демонстрационная программа. Конечно, вы должны откомпилировать демонстрационные программы прежде, чем пытаться их запустить. Иначе вы получите ошибку ClassNotFoundException.

Если вы используете J2ME Wireless Toolkit, вам нужно только поместить ваши исходные файлы в директорию проекта UIComponents/src/. Затем создайте проект. Wireless Toolkit откомпилирует все исходные файлы в директории sic/. Он запустит верификатор предварительной проверки и, наконец, разместит файлы .class в директории проекта classes/. С этого момента вы можете выполнять демонстрационные программы, перечисленные в основном окне MID-лета.

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

На рисунке 5.3 показан экран, который появляется, когда вы выбираете элемент AlertDemo из списка демонстрационных программ верхнего уровня. Этот экран отображает другой набор элементов - набор типов уведомлений - с помощью другого компонента MIDP, называемого ChoiceGroup. Экран, содержащий типы уведомлений, создается кодом в файле AlertDemo.java, показанном в листинге 5.2. Выбор одного из элементов на этом экране создает и отображает экземпляр этого типа компонента Alert.

Иерархия наследования, изображенная на рисунке 5.1, показывает, что ChoiceGroup не является ни Screen, ни Displayable. Это вид Item. Вспомните из главы 3, что Item является компонентом, который может быть агрегирован в Form. Обратите внимание, что класс AlertDemo дополняет Form, который дает ему возможность агрегировать элементы TextField и ChoiceGroup.

На рисунке 5.3 вы видите Form - экземпляр AlertDemo - который содержит объекты ChoiceGroup и TextField. Вспомните, что Form является единственным компонентом MIDP, который может включать другие компоненты. Таким образом, программа AlertDemo должна использовать Form для хранения элементов ChoiceGroup и TextField.



Блок прослушивания



Листинг 5.3. Блок прослушивания команд должен проверять активацию специальной команды List.SELECT_COMMAND, если приложение использует неявные списки

public .class UIComponentDemo extends MIDlet .
implements CommandListener
{
public void cornrnandAction (Command c, Displayable d)
{
Displayable displayable = null;
if (c == List.SELECT_COMMAND)
}
int index = mainMenu.getSelectedlndex ();

try i displayable = (Displayable)
Class.forName(demos[index]).new Instance));

Display display = Display.getDisplay(this);

display.setCurrent(displayable);

}
,catch (Exception e)
}
e.printStackTrace();
return;
}
}
else
{
return;
}
}

Названия типов выбора EXCLUSIVE и MULTIPLE говорят сами за себя. Реализации MIDP , визуализируют значки отображения выбора различно для исключающего и множественного списков. Исключающие списки выбора имеют кружок слева от текста элемента, сходный с селективными кнопками в приложениях AWT и Swing, и внутреннюю точку, показывающую выбранный элемент. Множественные списки выбора визуализируют компонент с квадратиком слева от текста элемента, сходным с кнопками для отметки в приложениях AWT и Swing.

Вы уже видели пример Alert в главе 3, здесь вы видите его вновь. Метод commandAction() класса AlertDemo создает пять различных уведомлений в зависимости от данных, которые пользователь вводит в экран Build Alert (Создание уведомления), показанный на рисунке 5.3. Конструктор класса Alert принимает значение AlertType, которое отражает тип уведомления, изменяющийся в зависимости от создания. Класс AlertType определяет пять констант, которые представляют собой возможные типы уведомлений, показанные в таблице 5.2.



Блок прослушивания



Листинг 5.4. Блок прослушивания команд должен находить ссылку на экземпляр любого экрана приложения, юкоторому он хочет вернуться

public void commandAction(Command c, Displayable d)
{
UIComponentDemo jiemo = UIComponentDemo.get Instance ();

Display display = Display.getDisplay(demo);

int timeSec;
int cimeMillis;
if (c == go)
}
// Уведомления не принимаются приложением, определяющим Commands.
String title = elements[type.getSelectedlndex()];
Alert alert = new Alert(title) ;;
alert.setString("A '" + title + "' alert");

timeSec = Integer.parselnt(tPref.getString());

timeMillis. = timeSec * 1000;
if (timeMillis <= 0)
}
timeMillis = Alert.FOREVER;
}
alert.setTimeout(timeMillis);

display.setCurrent(alert, AlertDemo.getlnstancef));

}if (c == back)
}
UIComponentDemo.getInstance().display!);

}

Если команда является командой Back (Назад), этот метод показывает предыдущий экран, пересылая экземпляр List, созданный в UIComponentDemo.java, в метод Display.setCurrent(). Если UIComponentDemo.getlnstance() не был объявлен static, получить ссылку на объект List будет сложно.

В соответствии с этой идиомой метод AlertDemo.getlnstance () возвращает ссылку на экземпляр, к которому дисплей должен вернуться, после того как уведомление будет закрыто. В таком случае может быть использована ссылка this. Но метод getlnstance() может стать доступным, если приложение будет позже усовершенствовано. Тем не менее, важным моментом является использование идиомы, которая делает ссылки на экраны легко доступными.

Первыми двумя строками метода commandAction () являются следующие:

UIComponentDemo demo = UIComponentDemo.get Instance ();

Display display = Display.getDisplay(demo);

Эти строчки используют ту же идиому для легкого получения ссылки на MID-лет. Класс UIComponentDemo определяет этот статический метод, который освобождает вас от вынужденного кодирования следующей строки каждый раз, когда вам понадобится создать ссылку на дисплей:

Display.getDisplay(UIComponentDemo.getMIDiet() ) ;

Конечно, это не единственный способ осуществлять экранную навигацию. Другой метод заключается в поддержке набора ссылок на объекты Displayable. Вы можете поместить объект Displayable в набор, когда вы сделаете его текущим отображаемым объектом. Чтобы перейти назад к предыдущему экрану, вытолкните его из стека и установите его текущим Displayable.

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



Поскольку экраны являются



Листинг 5.5. Поскольку экраны являются отображаемыми, метод getlnstanceO должен возвращать экранный объект некоторого вида. Этот возвращает экземпляр Form

import Java.util.Date;
import Java.util.Calendar;
import Java.util.TimeZone;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.DateField;
import javax.microedition.lcdui.Displayable;
import javax.
microedition.lcdui.Form;
/**
Демонстрирует использование класса DateField пользовательского интерфейса MIDP.
@смотри javax.microedition.Icdui.DateField
public class DateFieldDemo extends Form implements CommandListener
private Command back = new Command("Back", Command.BACK, 1);

private static Displayable instance;
private DateField date = new DateField("Date/Time in GMT",
DateField.DATE_TIME, TimeZone.getDefault () ) ;
/**
Конструктор.
*/
public DateFieldDemo()
}
super ("DateField Demo");

Calendar cal = Calendar.getlnstance();

date.setDate(cal.getTime()) ;
append(date);
addCommand (back);
setCcmmar.dListener (this) ;
instance = this;
}
/**
Возвращает один экземпляр этого класса. Вызов этого
метода перед созданием объекта возвращает пустой.указатель.
@Возвращает экземпляр этого класса.
*/
public static Displayable getlnstance ()
{
return instance;
}
public void commandAction(Command c, Displayable d)
{
if (c == back)
{
UI ComponentDemo.get Instance().display() ;
}
}
}

Прежде всего, обратите внимание, что DateFieldDemo дополняет класс Form. Конструктор просто добавляет объект DateField к форме и необходимая структура сформирована. Другие методы класса DateFieldDemo сходны с предыдущими примерами, так что я не буду их описывать здесь еще раз.

DateField является простым текстовым элементом, который отображает дату и время. На рисунке 5.4 показан экран дата/время, отображаемый DateFieldDemo.

Первая строка на рисунке 5.4 «Date/Time in GMT» («Дата/время в GMT») является меткой и определяется в первом аргументе конструктора. Вторая строчка является датой, а третья - временем. Конструктор no-arg DateFieldDemo в листинге 5.5 демонстрирует, как устанавливать дату в объекте DateField с помощью объекта Java.util .Calendar.

В этом примере указываются дата и время, потому что вызов конструктора устанавливает отображение обоих значений. Класс DateField определяет три константы (перечисленные в таблице 5.4), которые позволяют вам контролировать то, какая информация отображается.



Исходный код демонстрационной программы Ticker



Листинг 5.8. Исходный код демонстрационной программы Ticker

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.raicroedition.lcdui.Ticker;
import javax.raicroedition.lcdui.Form;
/**
Этот класс демонстрирует использование класса
Ticker пользовательского интерфейса MIDP.
@see javax.microedition.lcdui.Gauge
*/
public class TickerDerno extends Form
implements CommandListener
}
private String str = "This text keeps scrolling until the demo stops...";
private Ticker ticker = new Ticker(str);

private Command back = new Command("Back", Command.BACK, 1);

private static Displayable instance;
/**
Конструктор.
*/
public TickerDemo()
{
super("Ticker demo");

instance = this;
addCommand(back);
setTicker(ticker) ; setCommandListener(this);

{
...
}

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

Взглянув на рисунок 5.1 еще раз, вы заметите, что Ticker не является Item. Он является производным непосредственно от Java.lang.Object, что подсказывает вам, почему Ticker может быть привязан к дисплею, а не к экрану. Его не нужно извлекать из Item, поскольку он на самом деле не является чем-то, что размещено в Form.



Конструктор создает



Листинг 5.9. Конструктор создает объект изображения и пересылает его компоненту пользовательского интерфейса для отображения. Обратите внимание, что указание пути для изображения относительно к директории ресурсов этого проекта при установке с помощью инструментария J2ME Wireless Toolkit

import javax.microedition.lcdui.Command;
import javax.microedition.Icdui.ComraandListener;
import javax.microedition.Icdui.Displayable;
import javax.microedition.Icdui.Form;
import javax.microedition.Icdui.Image;
import javax.microedition.Icdui.Imageltem;
import Java.io.lOException;
/**
Этот класс демонстрирует использование класса
Imageltem пользовательского интерфейса MIDP.
Усмотри javax.microedition.Icdui.Imageltem
*/
public class ImageltemDemo extends Form implements CommandListener
{
private Imageltem imageltem;
/**
Конструктор.
@сбрасывает lOException, если указанный ресурс изображения не может быть найден.
public ImageltemDemo() throws lOException
*/
super("Imageltem Demo");

String path = "/bottle80x80.png";
Image image = Image.createlmage(path);

imageltem = new Imageltem)"Ship in a bottle", image,
Imageltem.LAYOUT_CENTER,
"Image not found");
append(imageltem);

addCommand(back);

setCommandListener(this) ;
instance = this;
}
...
}

В листинге 5.9 демонстрируется использование класса Imageltem компонента пользовательского интерфейса MIDP. Imageltem является подклассом Item, так что он должен быть размещен в Form, как было продемонстрировано в листинге.

Прежде чем вы сможете отобразить изображение, вы должны создать объект изображения. Класс javax.microedition.lcdui.Image определяет изображения. Чтобы создать экземпляр Image, укажите имя пути к файлу изображения. Файлы изображений должны храниться в формате Portable Network Graphics (PNG). J2ME поддерживает работу с изображениями только в этом формате.

Обратите внимание, что в листинге 5.9 имя пути файла изображения связано с директорией res/ директории проекта UlComponents. Директория res/ содержит все файлы ресурсов, включая файлы изображений. Если вы разместите свои изображения где-либо еще, они не будут найдены и ваша программа сбросит lOException, когда попытается открыть файл.

В листинге 5.9 конструктор создает Imageltem с помощью только что созданного объекта Image. Параметрами конструктора являются строка заголовка, которая отображается над изображением, объект изображения, указание размещения изображения и текстовая строка, которая будет показана в случае, если изображение не может быть отображено по какой-либо причине.

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



Несколько компонентов пользовательского



Рисунок 5.10. Несколько компонентов пользовательского интерфейса MIDP поддерживают отображение изображений. Здесь форма содержит компонент Image Item, который отображает изображение




Реализация предоставляет этот



Stringltem

Класс Stringltem определяет двухсоставный компонент дисплея. Объекты Stringltem содержат метку и какой-либо неизменяемый текст. На рисунке 5.7 показан экран, отображаемый классом StringltemDemo, который вы можете запустить из окна, в котором указаны основные компоненты пользовательского интерфейса.



Строковые элементы состоят из



Рисунок 5.7. Строковые элементы состоят из двух частей: текстовая метка и текстовое значение


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

Листинг 5.6. Строковые элементы являются формами

import javax.raicroedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Stringltem;
/**
Это? класс демонстрирует использование класса
Stringltem пользовательского интерфейса MIDP.
@see javax.microedition.lcdui.Stringltem
*/
public class StringltemDemo extends Form implements CommandListener
private Command back = new Command("Back", Command.BACK, 1);
private static Displayable instance;
private Stringltem si = new Stringltem("Stringltem's title",
"Immutable item text");
/**
Конструктор.
"/
public StringltemDemo()
super("Stringltem Demo"); append(si); addCoramand(back);
setCommandListener(this);
}
instance = this;
}
...
}

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



Существуют интерактивные и неинтерактивные



Листинг 5.7. Четырьмя параметрами, требуемыми для указания измерителя, являются его состояние, удобное для прочтения название, первоначальное значение и максимальное значение

import javax.microedition.Icdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.Icdui.Displayable;
import javax.microedition.Icdui.Form;
import javax.microedition.Icdui.Gauge;
/**
Этот класс демонстрирует использование класса
Gauge пользовательского интерфейса MIDP.
Усмотри javax.microedition.Icdui.Gauge
*/
public class GaugeDemo extends Form
implements CommandListener
}
private String gaugelLabel = new String("Interactive gauge");
private Gauge interactiveGauge = new Gauge("Interactive", true, 50, 15);
private String gauge2Label = new String("Non-interactive");
private Gauge staticGauge = new Gauge ("Static", false, 50, 25);
/**
Конструктор.
*/
public GaugeDemol)
}
super("Gauge Demo");
append(gaugelLabel); append(interacciveGauge);
append(gauge2Label); append(staticGauge);
}
addCommand(back); setCoramandListener(this);
instance = this;
}
...
}

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

public void setValue(int value) public int getValuel)



Описание всех компонентов


Имя класса компонента, Ul MIDP

Описание

Принадлежность к- API MIDP

Alert

Информационное всплывающее окно, может быть модальным или рассчитанным по времени

Высокоуровневый

AlertType

Определяет типы объектов Alert

Высокоуровневый

Canvas

Экран, в котором вы можете рисовать графические объекты и получать низкоуровневые события ключ/перо

Низкоуровневый

ChoiceGroup

Группа выбираемых элементов, находится в Form

Высокоуровневый

Command

Семантическая инкапсуляция событий пользовательского интерфейса

Как высокоуровневый, так и низкоуровневый

DateField

Компонент, который отображает дату и время

Высокоуровневый

Display

Класс, который извлекает структуры данных дисплея устройства

Высокоуровневый

Displayable

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

Как высокоуровневый, так и низкоуровневый

Font

Класс, предоставляющий шрифты для экранного текста

Высокоуровневый

Form

Экран, который собирает элементы для отображения

Высокоуровневый

Gauge

Тип визуального измерителя

Высокоуровневый

Graphics

Отображение контекста графических элементов устройства

Низкоуровневый

Image

Отображение изображений в формате Portable Network Graphics [PNG, переносимая сетевая графика]

Как высокоуровневый, так и низкоуровневый

Imageltem

Form, размещающий отображение изображения

Высокоуровневый

List

Список выбираемых объектов

Высокоуровневый

Screen

Абстрактный прародитель всех типов экранов

Высокоуровневый

Stringltem

Form, размещающий отображение строки

Высокоуровневый

TextBox

Многострочный, многоколонковый текстовой контейнер

Высокоуровневый

TextField

Однострочный текстовой контейнер

Высокоуровневый

Ticker

Отображение тикера

Высокоуровневый



Константы класса AlertType



Таблица 5.2. Константы класса AlertType, которые представляют собой возможные типы объектов Alert

Константа класса AlertType

Описание

ALARM (внимание)

Уведомление, которое отображает появление аварийного события

CONFIRMATION (подтверждение]

Диалоговое окно, которое запрашивает у пользователя подтверждение действия

ERROR (ошибка)

Диалоговое окно, которое уведомляет пользователя об ошибке

INFO (инфо)

Диалоговое окно, в котором присутствует информационное сообщение для пользователя

WARNING (предупреждение)

Диалоговое окно, которое показывает предупреждение

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

Тип уведомления не влияет на его поведение. Вы видели сходную организацию объектов Command в приложениях HelloWorld. Простое присвоение определенного типа Command не изменяет его поведение никоим образом. Выбор остается за вами как за программистом в создании последовательности тем способом, которым вы обращались со сходными типами объектов Command и Alert.

Если вы запустите программу, приведенную в примере, вы увидите, что экраны уведомлений не имеют команд, связанных с ними, на самом деле они и не могут их иметь. Вы также заметите, что экраны уведомлений исчезают через 5 секунд и возвращают экран Build Alert (Создание уведомления). Причина этого кроется в том, что программа установила по умолчанию 5-секундную длительность для всех уведомлений.

Величина длительности появления уведомления должна быть больше 0. Установление значения менее 0 приведет к IllegalArgumentException. Вы устанавливаете время истечения уведомления с помощью метода Alert.setTimeout(). Если вы укажете константу Alert.FOREVER, реализация поместит экранную клавишу Done (Готово) на уведомление. Уведомление будет оставаться открытым до тех пор, пока пользователь не нажмет Done (Готово).

В демонстрационной программе (которую вы можете найти в Web на сайте http://www.phptr.com), прокрутите вниз экран Build Alert (Создание уведомления) и вы увидите объект текстового поля, который содержит строку «5». Вы можете отредактировать этот объект TextField, который является другим компонентом пользовательского интерфейса, чтобы изменить значение времени истечения. Если вы укажете 0, приложение создаст уведомление с FOREVER (НИКОГДА) в качестве времени истечения.

TextField является последним новым компонентом, который вводит эта демонстрационная программа. TextField также является разновидностью Item, как показано на рисунке 5.1. TextField - это один из двух компонентов для ввода текста. Другим является TextBox, который мы обсудим далее в этой главе. Компоненты для ввода текста используют понятие ограничения ввода, которое ограничивает ввод до определенного набора разрешенных знаков, определяемого действующими ограничениями компонента.

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



Типы ограничений устанавливаемые


Константа ограничения

Описание

ANY

Любые буквенно-цифровые знаки

EMAILADDR

Только синтаксически правильный e-mail

NUMERIC

Только цифровые знаки

PASSWORD

Знаки не отображаются на дисплее

PHONENUMBER

Только цифровые знаки, реализация предоставляет задание формата

URL

Только синтаксически правильный LJRL

Вы определяете ограничения в конструкторе, чтобы создать экземпляр с желаемыми текстовыми атрибутами. Чтобы создавать экземпляры, которые поддерживают обработку комбинации текстовых категорий, перечисленных в таблице 5.3, укажите логический AND этих категорий. Вы можете определить установленные ограничения, исследовав поле CONSTRAINT_MASK объекта TextField.



Константы DateField



Таблица 5.4. Константы DateField для управления отображением информации о дате и времени

Константа DateField

Описание

public static int DATE

Отображает только дату

public static int DATE TIME

Отображает дату и время

public static int TIME

Отображает только время

Третьим аргументом конструктора DateField в листинге 5.5 является определение временных зон, объект Java.util.TiraeZone. Остерегайтесь того, что спецификация MIDP потребует от реализации поддержки только одной временной зоны. Вы должны знать, какие временные зоны поддерживает ваша реализация. Очень вероятно, что большинство реализаций MIDP поддерживает только одну временную зону.

Вызов конструктора DateField может определять временную зону, которая не поддерживается вашей реализацией MIDP. Если временная зона, которую вы указали в конструкторе, не поддерживается вашей реализацией MIDP, ваша программа все равно будет выполняться без ошибки или предупреждения, но временная зона объекта DateField будет представлять собой какую-либо зону, поддерживаемую реализацией, но не ту, которую вы запрашивали. И время, отображаемое на экране, будет отражать временную зону, используемую объектом DateField, вместо временной зоны, которую вы указали в вызове конструктора.

Объекты DateField являются редактируемыми. Чтобы отредактировать их,

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

Обратите внимание, что реализация размещает экранные клавиши Back (Назад) и Save (Сохранить) на экране. Такое представление интерфейса типично для всех редактируемых компонентов. Когда вы закончите редактирование и вернетесь к предыдущему экрану, время и дата, показываемые на дисплее, изменятся.

На главном экране DateFieldDemo, показанном на рисунке 5.4, вы можете прокрутить до поля времени и, нажав, выбрать его. Дисплей затем покажет экран, изображенный на рисунке 5.6.



Компоненты пользовательского



Таблица 5.5. Компоненты пользовательского интерфейса MIDP, которые используют изображения

Компонент пользовательского интерфейса MIDP

Описание

Alert

Изображение отображается вместе с текстом

ChoiceGroup

Изображение отображается слева от текста каждого элемента

List

Изображение отображается слева от текста элемента

Imageltem

Предоставляет контроль размещения самого объекта изображения

Классы ChoiceGroup и List могут отображать изображения как часть представления каждого из своих элементов. API для этих классов четкий и прямолинейный, так что я не буду показывать примеры для них. Та же идиома создания объекта изображения и передачи его компоненту применяется для всех компонентов пользовательского интерфейса MIDP, которые используют изображения.



Ticker



Ticker

Тикер (Ticker) является объектом, предоставляющим прокручиваемый текст наверху дисплея. TickerDemo в листинге 5.8 создает дисплей, показанный на рисунке 5.9.



Тикер размещается на дисплее но



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


Ticker связан с дисплеем, но не с экраном. Вы размещаете Ticker на экране с помощью метода Screen.setTicker (Ticker t), как показано в коде листинга 5.8.



с полным набором классов компонентов



Выводы по главе

Эта глава знакомит вас с полным набором классов компонентов пользовательского интерфейса MIDP. Существует две общие категории компонентов интерфейса пользователя: те, что расположены под Displayable в иерархии, и те, что находятся под иерархией Item.
Класс Screen происходит непосредственно из Displayable и определяет основные абстракции в MIDP. Приложения MIDP в своей основе базируются на экранах.
Form, вид Screen, является только разновидностью экрана, которая может включать другие компоненты. Form может содержать объекты String, изображения, определяемые классом Image, и объекты, чьи типы являются подклассами класса Item.
Стандартное приложение MIDP должно быть способно переходить с экрана на экран. Поэтому экраны должны уметь передавать ссылки в экземпляр объекта экрана, который дисплей должен отображать следующим. Стандартная идиома заключается в предоставлении статического метода, возвращающего такую ссылку в каждом классе, который определяет экран.
В главе 3 представлена полная программная структура и метафоры программирования. В главе 4 описан высокоуровневый API MIDP. Эта глава дополняет все это знакомством с компонентами MIDP, которые реализуют высокоуровневый API.
В следующей главе вы познакомитесь с низкоуровневым API MIDP.