Е. Буткевич Пишем программы и игры



страница9/15
Дата22.06.2019
Размер2.79 Mb.
#106112
ТипКраткое содержание
1   ...   5   6   7   8   9   10   11   12   ...   15
Глава 9. Организация потоков, работа с файлами

ких лишних символов, связанных с тем, что размер запрошенного нами массива (newbyte[150]) превышает объем текста. Красота!

Но, как показала практика, радоваться было рано. Запускаем наш пример на эму­ляторе от Nokia 7210, что более близко соответствует запуску на реальном аппа­рате, и видим печальную картину (рис. 9.2).










* (l) Nokia 72lO SOK


ёбаптО-ОПОПСЕЮ

ашхоохша







Рис. 9.1. Вы знали, что на телефоне можно читать книги?

Рис. 9.2. Осталось только научиться это читать...



Русский текст превратился в шифровку агентов МОСАДа, а снизу прилипли квад­ратики неиспользованных байтов массива. Отсюда делаем вывод, что кодировка win 1251 не совпала с той, что запрограммирована в аппарате Nokia. Единственное правильное решение в данной ситуации — пользоваться универсальной кодиров­кой, которая подходила бы ко всем моделям. Благо, такая кодировка существует. Ее название говорит само за себя — Unicode.

Встает вопрос: как теперь перевести текст с привычной глазу кириллицы на Unicode? Если текст находится в коде программы и является, например, заголов­ком формы или текстовым сообщением, то можно воспользоваться утилитой nati ve2asci 1 из Java SDK, который мы ставили в самую первую очередь.

Вызывается эта утилита из командной строки следующим образом:.. ЛЫп \ native 2ascii -encoding cpl251 f i T el .Java > file2. java, где параметр encoding указывает


Класс DatalnputStream 99

исходную кодировку, filel — исходный файл, f11е2 — конвертированный файл. Обратный перевод из Unicode на родной язык осуществляется с использованием параметра -reverse.

Например, после преобразования исходная строка form.append("Здравствуй, мир!"); будет выглядеть так:

form.append("\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439. \u043c\u0438\u0440!11);

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

Класс DatalnputStream

На этот раз мы воспользуемся классом DataInputStream, который является наслед­ником класса InputStream и реализует чтение основных типов данных. То есть, кроме основного метода readO, этот класс содержит методы readBooleanO, readByteO, readCharC), reacil nt C), readLong (), readShort C), readtlnsignedByte(), readUnsignedShort(). Преобразование будем выполнять с помощью кодовой строки, которая каждому символу кириллицы ставит в соответствие его код в универсальной кодировке. Таким образом, наша программа приобретает следующий вид:

import Java. io.DatalnputStream;

Form form - new FormC): Display display • Display.getDi splay(this) ; InputStream is = getClass0.getResourceAsStreamC7pushkin.txt"): DatalnputStream dis - new DatalnputStream(is): int i = 0;



char[] bArr « new char[150];

try { // преобразовать символы в Unicode и записать в массив while(true) bArr[i++] - (char)convert(dis.readUnsignedByteO):

)

catchdO Exception ioe) { }

String str = new String(bArr.O.i-l);

form.append(str):

char converttint ch) {

return (ch < 128) ? (char)ch : WIN1251_T0_UNIC0DE.charA«ch-128):

}

// кодовая строка для преобразования символа из winl251 в Unicode

private String WIN1251_TO_UNICODE -

"\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\

U040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffd\u2122\u0459\u203a\u045a\u045c\



100 Глава 9. Организация потоков, работа с файлами

u045b\u045f\u00a0\u040e\u045e\u0408\u00a4\u0490\u00a6\u00a7\u0401\u00a9\u0404\u00ab\u00ac\ U00ad\u00ae\u0407\u00b0\u00bl\u0406\u0456\u0491\u00b5\u00b6\u00b7\u0451\u2116\u0454\u00bb\ U0458\u0405\u0455\u0457\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\ U041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\ U042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\ U043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0439\u0447\ U0448\ u0449 \u044a\u044b\ u044c\u044d\ u044e\ u044f";

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






Как гений чистой красоты.
<$гО) Nokia 721.0 S&tC-



Рис. 9.3. Демонстрация практической пользы от Unicode

Рис. 9.4. Так телефон признается в своей несостоятельности



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

Класс BookReader 101

щаем в памяти программы и захватываем область памяти, равную по объему все­му содержимому ресурса. Такой подход сводит на нет все преимущества потока, способного выдавать данные мелкими порциями по мере надобности. В качестве эксперимента помещаем в ресурс небольшой рассказик объемом 20 килобайтов и пробуем, что из этого получится. Стандартный эмулятор после двухминутных раздумий все же справился с задачей (поверьте на слово или проверьте, если хо­тите), а эмулятор от Nokia опять сошел с дистанции, выдав вот такое милое сооб­щение (рис. 9.4).



Класс BookReader

Итак, пишем приложение для удобного чтения литературы с экрана телефона. Для начала хотелось бы уменьшить шрифт, выводимый на экране. Смену шрифта поддерживает класс Graphics, содержащий метод setFont(Font font). В примерах мы пользовались объектом класса Form, который не поддерживает прорисовку объектов класса Graphics. В нашем случае воспользуемся отображаемым классом Canvas, который мы уже рассматривали в одной из первых глав.

Остановимся на классе Font, который и представляет отображаемый шрифт. По­лучить новый шрифт можно с помощью метода Font getFont(int face, int style. int size), параметры которого определяют, как будут выглядеть символы:


  • face — вид шрифта определяется константами FACE_SYSTEM, FACE_MONOSPACE или
    FACE_PROPORTIONAL;

  • style — стиль шрифта задается константой STYLE_PLAIN или комбинацией кон­
    стант STYLEJOLD, STYLEJTALIC и STYLEJJNDERLINED;

  • size— размер шрифта задается одной из констант SIZE_SMALL, SIZE_MEDIUM или
    SIZE_LARGE.

Если в классе Form форматирование текста по размеру экрана выполнялось авто­матически, то сейчас вся ответственность за корректное позиционирование тек­ста переходит к нам. Класс Font содержит ряд методов для определения размера текста в пискселах:

  • int getHeight() — возвращает высоту строки шрифта в пикселах;

  • int charWidth(char ch) — возвращает ширину символа ch в пикселах;

  • int charsWidth(char[] ch. int offset, int length) — возвращает ширину набора
    символов, содержащихся в массиве ch, начиная с позиции offset длины 1 ength;

  • int stri ngWidth(String str) — возвращает ширину строки str в пикселах;

  • int substringWidthCString str, int offset, int Ten) — возвращает ширину
    фрагмента строки str, начинающегося с позиции offset и имеющего длину
    1еп.

В классе BookCanvas реализуем следующую логику отображения текста. В стеке Page Index будем хранить смещение от начала текста для каждой страницы, что­бы организовать возможность обратной прокрутки текста. Функция paint чи­тает из потока данных по одному слову и отображает его, если на экране доста­точно пространства. Клавиша 2 осуществляет переход к следующей странице

102 Глава 9. Организация потоков, работа с файлами

текста, клавиша 1 — к предыдущей странице, а клавиша 0 возвращает к началу текста:

import Java.io.InputStream: import java.io.EOFException; import java.io.IOException; import java. util .Stack;

public class BookCanvas extends Canvas {

private Stack Pagelndex; // стек смещений страниц от начала текста

public BookCanvasO { Pagelndex = new StackO: t

protected void paint(Graphics g) {

// получить ширину и высоту рабочей области экрана int gw = g.getClipWidthO: int gh = g.getClipHeightO:

// создать шрифт

Font font = Font.getFont(Font.FACE_MONOSPACE.Font.STYLE_PLAIN.Font.SIZE_SMALL):

// установить шрифт g.setFont(font):

// инициализировать поток

InputStream is = getClass0) .getResourceAsStreamC7story.txt"):

MyData Input Stream mdis = new MyDatalnputStream(is);

int x=O.y=O: // текущее положение вывода на экране int offset=0: // смещение по исходному тексту

// очистка экрана g.setColor(255.255.255): g.fillRect(O.O.gw.gh): g.setColor(O.O.O):

// получить смещение для очередной страницы Ш IPagelndex.emptyO)

offset=((Integer)PageIndex.peekO) .intValueO :

try{

// сместиться по тексту до необходимой страницы



Класс BookReader 103

mdis.skipBytes(offset); } catchUOException ioe) {}

String sWord; do {

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

sWord = mdis.readWordO;

// продвинуть смещение по файлу на одно слово

offset+=sWord.1ength();

// если текущая позиция, и пиксельная ширина слова II не выходят за экран if(x+font.stringWidth(sWord)<=gw) {

// отобразить слово, начиная с текущей позиции

g.drawString(sWord, x.y. g.TOPjg.LEFT):

// сместить текущую позицию на пиксельную ширину слова

x+-font.stringWidth(sWord);

// если слово - последнее в строке, перевести текущую позицию

// в начало новой строки на дисплее

if (mdis.b_endLine) { y+=font.getHeight(): x=0; }

)

П слово не входит ло ширине с текущей позиции

else {


// перейти на новую строну

y+=font.getHeight();

// если на экране есть место для новой строки.

// отобразить слово с начала строки

if(y+font.getHeight()

(sWord. O.y. g.TOP|g.LEFT):

// перевести текущую позицию на ширину слова

x=font.stringWidth (sWord);

} // повторять до тех пор. пока есть иесто для новой строки

// или не кончится файл

} while(y+font.getHeight()!mdis.b_endFile) ;

int index; // смещение начала страницы

// если цикл закончился чтением слова.

// которое не было отображено на экране,

// сместить начало новой страницы назад на одно слово

if(mdis.b_endl_ine) index=offset; else index=offset-sWord.length();

// поместить в стек начало следующей страницы

if(!mdis.b_endFile) Pagelndex.push(new Integer( index));

// закрыть поток

{

mdis. closeO;



104 Глава 9. Организация потоков, работа с файлами

} catchCIOException ioe) {}

public void keyPressed(int keyCode){ // обработка нажатий клавиш switch(keyCode) { case KEYJUM1:

// удалить из стека начала следующей и текущей страницы PageJ.ndex.popO;

1f(!PageIndex.empty()) Pagelndex.pop(); repaint О ; break; case KEY_NUM2:

// перерисовать экран (начало следующей страницы уже в стеке) repaintО: break; case KEYJUMO:

// очистить стек и перерисовать экран PageIndex.removeAl1 Е1ements (); repaintO: break:

J

Поток в данном случае представлен классом, наследованным из Data lnputSt ream и реализующим функцию чтения слова readWordO. При чтении очередного слова функция выставляет флаги конца строки и конца файла. Сам класс выглядит сле­дующим образом:

import java.io.InputStream: import java.io.DatalnputStream; import java.io.EOFException; import java.io.IOException:

public class MyDatalnputStream extends DatalnputStream {

private String WIN1251_T0_UNIC0DE = "...": // кодовая строка

// преобразования символов

public boolean b_endl_ine » false; // флаг конца строки public boolean b_endFile • false; // флаг конца файла

public MyDataInputStream(InputStream is) { super(is);

public String readWordO {

int 1-0; // счетчик символов в текущем слове




Класс BookReader

105


b_endFile = false:

char[] word = new char[100]; // массив для чтения слова

try {

// читать слово посимвольно до пробела или перевода строки do word[i++]=(char)convert(readUnsignedByte()); while (word[i-l]!=32 && word[i-l]!=10):



) catch(EOFException ioe) {i--: b_endFile = true:} catchUOException ioe)

if(word[i-l]==10) b_endLi ne=true: else b_endLine=false: return (new String(word.O.i )):



char convert(int ch) {

return (ch < 128) ? (char)ch

1

WIN1251 _T0 JNIC0DE. charAt(ch-128);



При чтении данных потока могут быть сформированы следующие исключения:



  • EOFExcepti on — формируется при достижении
    конца файла;

  • IOException — базовое исключение для всех
    функций ввода-вывода.

В программе мидлета осталось лишь создать объект класса BookCanvas и установить его в ка­честве текущего дисплея, как мы уже неодно­кратно делали. Запускаем получившееся при­ложение на эмуляторе и получаем в пользова­ние воттакую замечательную читалку (рис. 9.5).

Рис. 9.5. «Алло! Мистер Шухарт?»

Совершенству, как известно, предела нет, поэто­му некоторые моменты остались на самостоя­тельную доработку. Непроработана ситуация, когда длина слова превышает размер экрана — здесь нужна логика деления слова на части и переноса на следующую строку. При чтении последней страницы очередное смещение в стек не заносится, поэтому при прокрутке текста на­зад с конечной позиции одна страница будет пропущена. Кроме этого, текст не должен содер­жать в конце пробелов или переводов строки: это может стать причиной формирования исключе­ния при просмотре последней страницы. Еще можно поработать над более удобным, интуи­тивным перемещением по тексту, задействовав не цифры, а навигационные клавиши аппарата, помеченные стрелками. Для разных моделей те­лефонов эти клавиши формируют разные коды,

106 Глава 9. Организация потоков, работа с файлами

поэтому лучше пользоваться API-константами конкретной модели. На API-функ­циях некоторых моделей мы остановимся позже.



Класс ByteArraylnputStream

На примерах мы рассмотрели работу с файлом-ресурсом, который предоставлял приложению все необходимые данные. На самом деле, поток может быть сфор­мирован из любых данных и представлен в виде последовательности байтов. Еще один класс, который порожден из базового класса InputStream и представляет по­ток в виде открытого массива байтов, — это ByteArraylnputStream.

Данный класс предоставляет все объявленные, но не реализованные функции класса InputStream, позволяя получить заранее число доступных байтов потока, пометить позицию возврата для повторного чтения данных.

Внутренняя структура потока открыта для нас. Программисту доступны следу­ющие поля:



  • byte[ ] buf — массив байтов для хранения данных, указанный при создани и потока;
    ш int count — число доступных байтов потока;

  • int mark — индекс текущей помеченной позиции для повторного чтения;

  • int pos — индекс следующего байта потока для чтения.

Использование объектов класса ByteArraylnputStream мы рассмотрим в следующей главе.

Класс OutputStream

До сих пор мы работали с потоком только одного направления — от ресурса к при­ложению. Все рассмотренные классы, так или иначе, связаны со словом Input (ввод), а методы чтения данных — со словом read. Вся система ввода зеркально отобража­ется на работу с потоком обратного направления. Поток вывода данных сохраняет всю иерархию классов потока ввода, с той лишь разницей, что в названиях классов вместо слова Input фигурирует слово Output: OutputStream, ByteArrayOutputStream, DataOutputStream. Каждому методу чтения, содержащему в названии слово read, со­ответствует аналогичный метод записи, только со словом wri te.

Функции потоков чтения skip, avaiI able, mark и reset для потоков вывода не акту­альны, вместо них потоки вывода реализуют лишь одну функцию flush(), кото­рая очищает все данные выходного потока.

* * *

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



Глава 10

Ввод текста, работа со строками

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

В наш век научно-технического прогресса сотовый телефон практически всегда под рукой, в отличие от обычной авторучки. Часто ли вы сталкивались с ситуаци­ей, когда нужно записать какую-то важную информацию: почтовый адрес, e-mail, названия лекарств, маркировку деталей, да мало ли что? Стандартная записная книжка мобильного телефона рассчитана лишь на запись телефонного номера и не очень заковыристых имени и фамилии его обладателя. Можно, конечно, ис­пользовать не по назначению ремайндер, если он есть, или писать текст в теле сообщения SMS, но в таком случае потерять информацию так же легко, как и слу­чайный клочок бумаги. Мы же попробуем сейчас написать свою адресную книгу, расширив количество хранимой информации согласно нашим требованиям.

Стандартные средства предлагают нам два варианта для реализации полей ввода текста: TextField и TextBox. Рассмотрим подробно каждый из них.



Класс TextBox

Класс TextBox является потомком класса Screen и представляет собой экран, по­зволяющий вводить и редактировать текст с клавиатуры аппарата. При создании объекта в конструкторе нужно указать заголовок поля ввода, начальное содержи­мое поля, максимально допустимое количество вводимых символов и ограничи­тель для вводимой информации:

Text Box (String title. String text, intmaxSize, int constraints)

Ограничители позволяют заранее задать тип вводимого текста. Так, например, если информация содержит только цифры, то буквы не будут появляться в этом поле при нажатии на клавиши. Для полей ввода доступны следующие ограничи­тели, которые определяются константами класса TextField:



  • ANY — допускается ввод любых символов без ограничений;

  • EMAILADDR — используется для ввода адреса электронной почты;

  • NUMERIC — применяется для ввода целых чисел. Клавиша * позволяет вводить
    отрицательные числа и добавляет знак - перед числом;

108 Глава 10. Ввод текста, работа со строками

  • PHONENUMBER — ввод телефонного номера. Поле ввода такого типа привязано
    к стандартному вводу телефонного номера аппарата. Это значит, что в меню
    автоматически появится команда поиска номера в записной книжке Search;

  • PASSWORD — используется в комбинации с другими константами для скрытия вво­
    димой информации. Вводимые символы отображаются звездочками, как при
    вводе PIN-кода. Комбинации констант задаются с помощью оператора | (или),
    например: NUMERIC | PASSWORD;

  • URL — ввод сетевого адреса интернет-страницы в формате URL;

  • CONSTRAI NT_MASK — маска ограничителя; константа, используемая для получения
    текущего ограничителя поля иззначения, возвращенного методом GetConst гаi nts ().
    Маска накладывается с помощью оператора & (и) на возвращенное значение
    и отбрасывает флаги модификаторов ограничителя, например, такие как PASSWORD.

После того как объект поля ввода создан, он может быть отображен на экране теле­фона так же, как и все отображаемые объекты, наследованные из класса Di spl ayabl e, с помощью метода класса Display setCurrent(TextBox). Если вводимый текст превы­шает размер экрана, система сама обеспечит прокрутку без каких-либо усилий с нашей стороны. Дальнейшая работа с созданным объектом обеспечивается следу­ющими методами класса TextBox:

  • void delete (int offset, int length) — удаляет из поля ввода length символов,
    начиная с позиции offset;

  • int getCaretPositionO — возвращает текущее положение позиции ввода (0 —
    начало текста);

  • int getCharsCchart] data) — копирует весь текст из поля ввода в символьный
    массив data, возвращает число скопированных символов. Текст, отображаемый
    в поле ввода, не изменяется;

  • int getConstraints() — возвращает текущий ограничитель поля ввода;

  • int getMaxSi ze() — возвращает максимально допустимое количество вводимых
    символов;

  • String getStringO) — возвращает содержимое поля ввода в виде строки;

  • void insert(Stringsre, int position) — вставляет строку src в поле ввода, на­
    чиная с позиции position. Если указанная позиция меньше или равна нулю, то
    строка вставляется в начало поля, если позиция больше количества уже вве­
    денных символов, то строка вставляется сразу за последним символом;

  • void insert(char[] data, int offset, int length, int position) — вставляет в поле
    ввода с позиции, заданной переменной position, length символов из массива data,
    начиная с символа с индексом offset. Логика вставки, как в предыдущем методе;




  • voidsetChars(char[] data, intoffset, int length)—очищает поле ввода и встав­
    ляет length символов из массива data, начиная с позиции массива offset;

  • void setConstraints(int constraints) — устанавливает ограничители поля ввода;

  • int setMaxSized'nt maxSize) — устанавливает максимально допустимое количе­
    ство вводимых символов;

  • voidsetString(Stringtext) — заменяет текущее содержимое поля ввода на строку,
    заданную параметром text;

  • int size() — возвращает количество символов, содержащееся в поле ввода.

Класс TextField 109

Класс TextField

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

Разница между объектами TextBox и TextField такая же, как и между рассмотрен­ными ранее объектами List и ChoiceGroup. Они реализуют один и тот же набор методов, а различаются только тем, что первый является самостоятельным ото­бражаемым объектом иерархии класса Displayable, а второй для демонстрации должен быть включен в форму.

Класс TextFi el d расширяет рассмотренный нами в программе слайд-шоу класс Item, а это значит, что объект класса TextField можно включить в обычную форму (class Form) с помощью стандартного метода формы append.

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

import javax.microedition.lcdui.TextField:



private Display display:

private Form form:

private Command equal:

private MyCommandListener cl = new MyCommandListenerO;

private TextField argl.arg2.action: // поля ввода

public void startAppO {

// создать форму калькулятора display - Display.getDisplay(this): form = new Form ( "Calculator"): form.setCommandListener(cl):

// команда, запускающая вычисления

equal = new Command("Equal". Command.OK, 1):

form.addCommand(equal);

// поле ввода первого аргумента

argl = new TextFieldC'Argumentl"."". 5.TextField.NUMERIC);

form.append(argl):

// поле ввода символа операции

action = new TextField("Action"."".l.TextField.ANY):

form.append(action):

// поле ввода второго аргумента

arg2 = new TextField("Argument2"."".5.TextField.NUMERIC);

110 Глава 10. Ввод текста, работа со строками

form.append(arg2);

display.setCurrent( form);

private class MyCommandListener implements CommandListener public void commandAction(Command с Displayable d)

if(c==equal) { // запустить вычисление // форма для экрана результата операции Form resForm = new Form("Result"); resForm.setCommandListener(cl); Command back = new CommandC'Back",Command.BACK.1); resForm. addCommand( back):

// проверить заполнение всех полей ввода if(argl.size()==O || arg2.s1ze()—G [| action.size()==O) {

// ошибка, не все поля ввода заполнены

resForm.appendC "Missing argument"):

// отобразить сообщение

display.setCurrent(resForm);

// выйти


return; ■

// получить символ операции char[] act = new char[l]; action.getChars(act);

// преобразование аргументов

int first = Integer.parselnt(argl.getStringO):

int second - Integer.parseInt(arg2.getString());

int res = 0:

switchCact[O]) {

// выполнить действие в соответствии // с символом операции case '+':

res - first + second: break: case '-'-.

res = first - second: break; case '*':

res = first * second; break;

Класс AddressBook

111


case '/';

res = first / second; , . break; default:

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

resForm.append( "II legal Operation"):

// отобразить сообщение

display.setCurrent(resForm);



II выйти

return;


// преобразовать результат в строку и добавить в форму resForm.appendU(new Integer(res)).toStringO);: // отобразить результат display.setCurrent(resForm); else

// команда Back формы результата приводит сюда // отобразить первоначальную форму di splay.setCurrent(form);






Вид и размещение полей ввода на экране зависит от конкретной модели аппарата. На стандартном эмуля­торе наш калькулятор будет выглядеть следующим об­разом (рис. 10.1).

Казалось бы, можно радоваться, поскольку мы полу­чили бесплатный калькулятор, который отсутствует во многих моделях. Главная проблема состоит в том, что виртуальная машина CDLC не поддерживает вы­числений с плавающей точкой. Другими словами, наш калькулятор сможет работать только с целыми числа­ми, отбрасывая при делении дробную часть. К сожа­лению, такой калькулятор сгодится лишь для подсче­та сдачи в магазине.



Класс AddressBook

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

Рис. 10.1. Так выглядит простейший калькулятор

112 Глава 10. Ввод текста, работа со строками

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

Для начала продумаем сценарий смены экранов. Приложение стартует с экрана со списком всех имен, уже добавленных в записную книжку. Команда Ok демонст­рирует форму, содержащую все параметры записи, соответствующей выбранно­му имени, а команда Add начинает выполнение сценария добавления новой запи­си. Сценарий добавления записи предоставляет по очереди несколько экранов для ввода каждого из параметров, команда Next выполняет переход к следующему эк­рану, а команда Back — возврат к предыдущему.

Рассмотрим основной класс мидлета Address Book, который в качестве полей со­держит экран списка имен, экраны сценария добавления записи, а также команды перехода между экранами. Стартовый метод startAppC) инициализирует экраны и команды и создает список имен из уже имеющихся записей:



import javax.microedition.midlet.MIDlet:

import javax.microedition.icdui .Command:

import javax.microedition.lcdui .CommandUstener;

import javax.microedition.lcdui.Display:

import javax.microedition.lcdui .Displayable:

import javax.microedition.lcdui.Form:

import javax.microedition.lcdui .List:

import javax.microedition.lcdui .TextField:

import javax.microedition.lcdui .TextBox:

import Java.iо.ByteArrayOutputStream:

import java.io.ByteArraylnputStream;

import java.io.DataOutputStream:

import java.io.DatalnputStream;

import java.io.IOException:

1mport j avax.microed i t i on.rms.RecordStore:

import javax.microedition.rms.RecordEnumeration:

import javax.microedition.rms.RecordComparator:

import javax.microedition.rms.RecordStoreException:

public class AddressBook extends MIDlet implements CommandListener

{

private Display display: // менеджер дисплея

private RecordStore recordStore; // хранилище записей

private List nameList: // список имен

private int reclndexes[]: // массив ID записей.

П соответствующий списку имен

private Command add.ok.next.back: // команды переходов между экранами

private TextBox tbName.tbPhone.tbEMail: // экраны ввода параметров записи

Класс AddressBook 113

public void startAppO { try {

// открыть хранилище записей с именем "Address-Book" recordStore - RecordStore.openRecordStoreCAddress-Book", true); } catch (RecordStoreException rse) {}

// получить ссылку на менеджер дисплея display • Oisplay.getDisplay(this):

// создание объектов команд переходов ok - new CommandC'Ok", Command.OK, 1); add = new CommandC 'Add", Command.BACK, 1): next = new Command("Next". Command.OK. 1): back » new Command("Back", Command.BACK. 1):

// поле ввода имени

tbName - new TextBox("Name:"."".15,TextField.ANY):

tbName.addCoiraiand(next):

tbName.addCommand(back);

tbName. setCommandL i stener(this):

// поле ввода номера телефона

tbPhone = new TextBox("Number:"."".15.TextField.PH0NENUMBER):

tbPhone.addComma nd(next):

tbPhone.addComma ndCback):

tbPhone. setComrnandListenerCthis^:

// поле ввода электронной почты

tbEMail = new TextBox("E-Mail: "."".35.TextField.EMAILADDR):

tbEMail.addCommand(next);

tbEMai1 .addCommand(back);

tbEMail.setCommandListener(this):

// создать список имен

BuildNameListO;



II отобразить список имен на экране

display.setCurrent(nameLi st); }

Обратим внимание, что если в предыдущей программе для ввода информации мы использовали несколько объектов класса TextField, которые добавляли в одну форму, то теперь каждое поле ввода представляет собой отдельный экран класса TextBox. Каждый экран содержит команды Next и Back, которые осуществляют пе­реходы от одного экрана ввода к другому.

Создание списка имен реализовано в методе Bui ldNameListO, который создает и инициализирует новый объект списка, затем получает все записи хранилища, отсортированные по именам в алфавитном порядке. Имена добавляются в список

114 Глава 10. Ввод текста, работа со строками

имен, a ID соответствующих записей — в массив reclndexes, чтобы при надобнос­ти можно было легко получить все остальные параметры необходимой записи:

// метод создает список имен из адресной книги private void BuiidNameList O { И создать объект списка

nameList = new List("Address-Book". List.IMPLICIT): nameList.setCommandListener(this): // команда добавления записи nameList.addComiTiand(add); // команда получения параметров записи nameLi st.addCommand( ok); try {

II получить количество записей

int size = recordStore.getNumRecordsO:

// создать массив для хранения ID записей

reclndexes = new int[size]:

// создать объект компаратора в алфавитном порядке

Alphabetical Ordering comparator = new Alphabet icalOrderingO;



t) получить список записей хранилища

RecordEnumeration re » recordStore.enumerateRecords

(null, comparator, false); // индекс массива хранения ID записей int 1=0;

// бесконечный цикл; выход из цикла происходит // по формированию исключения получения ID следующей записи, // после того как было получено ID последней записи while(true) {



И получить ID следующей записи

int id = re.nextRecordldO;

// записать ID в массив

reclndexes[i++]=id:

// получить запись no ID

byte[] record = recordStore.getRecord(id);

// преобразовать запись в байтовый поток

ByteArraylnputStream bais = new ByteArraylnputStream(record);



I) создать поток, поддерживающий чтение по типу

Datal nputStream dis = new DatalnputStream(bais);

// считать из потока первую строку и добавить в список

nameList.appendtdis.readUTFO.null):

catch(RecordStoreException rse) () catchdOException ioe)' {}

Класс AddressBook

115


Алфавитный порядок добавления записей в список имен реализован с помощью компаратора, представленного классом Alphabetical Ordering:

// класс компаратора записей по алфавиту

private class AlphabeticalOrdering implements RecordComparator { It метод сравнения записей public int compare(byte[] reel. byte[] rec2) { // преобразовать записи в байтовый поток ByteArraylnputStream baisl - new ByteArraylnputStream(recl): ByteArraylnputStream bais2 = new ByteArrayInputStream(rec2); // создать потоки, поддерживающие чтение по типу DatalnputStream disl = new DatalnputStream(baisl); Oata lnputStream dis2 = new DataInputStream(bais2): // строки имен String name! = null: String name2 = null; try {




// считать строки с именами namel = disl.readUTF О: name2 = dis2.readUTF О :

catch (IOException ioe) {} // лексикографическое сравнение строк int result = namel.compareTo(name2); if (result < 0)

// первая запись предшествует второй return RecordComparator.PRECEDES: else

if (result == 0)

// записи содержат идентичные имена return RecordComparator.EQUIVALENT: else

// вторая запись предшествует первой return RecordComparator.FOLLOWS:

J


Рис. 10.2. Если встроенная

записная книжка вас не

устраивает, можно

создать свою



Таким образом, остается реализовать только блок про­слушивания команд, который определяет действия для осуществления переходов между экранами. Если же создать пустой метод commandAction, то приложение уже можно компилировать. Запустив приложение, при на­личии записей, мы могли бы увидеть список имен, по которому можно перемещаться с помощью навигаци­онных клавиш (рис. 10.2).

Осталось сделать последний шаг и написать олок про­слушивания команд, который будет контролировать



116 Глава 10. Ввод текста, работа со строками

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

// блок прослушивания команд

public void commandAction(Command с. Displayable d) {

// команда Add отображает экран ввода имени 1f(c==add) display.setCurrent(tbName):

// команда Ok if(c==ok) {

// если команда вызвана из экрана со списком имен if(d==nameList) { try {

// получить в массиве reclndexes ID необходимой записи

int id = recIndexes[nameList.getSelectedIndex()];

// считать необходимую запись

byte[] record = recordStore.getRecord(id):

// преобразовать запись в байтовый поток

ByteArraylnputStream bais =

new ByteArraylnputStream(record):

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

DatalnputStream dis = new DatalnputStream(bais) ;

// создать форму для отображения параметров

Form infoForm = new FormC'"):

// считать строки с параметрами и добавить в форму

infoForm.append(dis.readUTF()+"\n");

i nfoForm.append(dis.readUTF()+"\n");

infoForm.append{dis.readUTFC)+"\n");

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

i nf oForm. addCommand(ok) ;

1nfoForm.setCommandL i stener(this ) :

// отобразить форму на экране

display.setCurrent(i nfoFdrm):

}

catch(RecordStoreException rse) {} catchdOException ioe) {} } else { // если команда вызвана из формы параметров записи.



II вернуться к списку имен di spl ay. setCurrent (nameLi st);

// команда Next: перейти к следующему полю ввода

if(c==next) {

// из поля ввода имени к полю ввода номера телефона if(d==tbName) display.setCurrent(tbPhone):

Класс AddressBook 117

// из поля ввода номера телефона к полю ввода e-mail if(d==tbPhone) display.setCurrentCtbEMai1): // из поля ввода e-mail if(d==tbEMail) {

// создать байтовый поток вывода

ByteArrayOutputStream baos - new ByteArrayOutputStreamt); // создать поток вывода, поддерживающий запись по типу DataOutputStream dos = new DataOutputStream(baos): try {

// записать введенные параметры в поток вывода dos.writeUTF(tbName.getStringO); dos.writeUTF(tbPhone.getStringС)):' dos. writeUTF( tbEMail. getStringO); // добавить запись в хранилище recordStore.addRecord(baos.toByteArray(). 0. baos.sizeO):

1

catchUOException ioe) {}



catch (RecordStoreException rse) {}

// создать список имен

BuildNameListO;

// отобразить список имен на экране

display.setCurrent(nameList);

// команда Back: возврат к предыдущему полю ввода

if (с—back) {

// из поля ввода имени к списку имен

1 f(d==tbName) display.setCurrent(nameList);

// из поля ввода номера телефона к полю ввода имени

if(d==tbPhone) display.setCurrent(tbName);

// из поля ввода e-mail к полю ввода номера телефона

if(d-—tbEMail) display.setCurrent(tbPhone):

На этом пока остановимся. Теперь все команды должны работать, можно запус­кать и пробовать. Экран ввода параметров записи будет выглядеть следующим образом (рис. 10.3).

После того как мы ввели параметры записи и добавили ее в хранилище, введенное имя записи появится в списке. Можно выбрать его, используя навигационные кла­виши, и с помощью команды Ok вывести на экран параметры записи (рис. 10.4).

Основное направление движения задано. Дальше вы можете продолжить самосто­ятельно. Первым делом нужно реализовать поиск записи по имени или его фраг­менту с помощью фильтра записей хранилища. Не лишней при добавлении новой записи будет проверка, существует ли уже запись с подобным именем. Такую про­верку можно организовать с помощью того же фильтра. Я уверен, что богатая фан-



118

Глава 10. Ввод текста, работа со строками



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













РИС. 10.3. Экран ввода параметров записи на стандартном эмуляторе

Рис. 10.4. Информация из записной книжки выведена на экране



Один большой минус заключается в том, что, в общем случае, доступа из мидлета к стандартной адресной книге телефона и ее записям нет, то есть невозможно по­лучить программно записи из стандартной адресной книги. Иногда, если повезет, такой доступ предоставляет API производителя конкретной модели телефона.

В этой главе мы рассмотрели на примерах работу с полями ввода, представленны­ми объектами классов TextBox и TextFi eld. Даже внебольшой программе адресной книги нам понадобились знания почти из всех предыдущих глав. Мы использова­ли интерфейс высокого уровня для связи с пользователем и реализации логики смены экранов, создали список имен с помощью объекта класса List, организова­ли долговременное хранение записей с использованием RMS, осуществили за­пись и чтение данных с помощью потоков ввода-вывода. Возможности мобильно­го языка J2ME очень ограничены, нетрудно разобраться со всеми и попробовать их в действии. Это поможет реально оценивать свои силы при проектировании функциональности программ.




Каталог: Техника -> Информационные%20технологии
Информационные%20технологии -> Методические рекомендации по построению систем защиты узлов интернет 1 требования к системе защиты узла интернет 2
Техника -> Учебная программа для специальности: 1-23 01 73 Средства массовой информации
Техника -> Ремонт китайских телефонов
Техника -> Для профилактики и лечения насморка Зачем промывать нос во время насморка?
Техника -> Учебная программа для специальности: 1-23 01 73 Средства массовой информации


Поделитесь с Вашими друзьями:
1   ...   5   6   7   8   9   10   11   12   ...   15




База данных защищена авторским правом ©vossta.ru 2022
обратиться к администрации

    Главная страница