четверг, 15 апреля 2010 г.

Глава 3 Дизайн интерфейса пользователя


Глава 3 Дизайн интерфейса пользователя

            В главе 1, Быстрый старт, мы использовали Eclipse plug-in для того чтобы создать программу «Hello, Android» за несколько минут. В части II, мы создадим более существенный пример: игру Sudoku, затем будем постепенно добавлять функционал к игре, тем самым вы изучите много аспектов программирования под Android. Мы начнем с пользовательского интерфейса.
            Вы можете найти исходный код примера используемого в этой книге на http://pragprog.com/titles/eband



3.1 Введение в пример Sudoku

            Sudoku - это хороший пример программы для Android, потому что сама игра простая. Вы имеете решетку из 81 плиток (9 строк и 9 столбцов), и вы пытаетесь заполнить её так, чтобы каждый столбец, каждая строка, и каждое поле 3 на 3 имела по одной из каждой цифры от 0 до 9. Когда игра начинается, некоторые из ячеек уже заполнены цифрами, а игрок должен заполнить остальные. Головоломка Sudoku имеет только однозначное решение.
            Sudoku обычно играют с карандашем и бумагой, но компьютеризированные версии тоже довольно популярны. С бумажным вариантом, легко сделатьошибку, и когда это произойдет, вы должны вернуться назад и стереть большую часть из вашей работы ластиком. В Android варианте, вы можете изменять ячейки по мере необходимости без боязни протереть дырку ластиком.
            Android Sudoku (см. рисунок 3.1) подскажет, если пользователь застрянет. Однако эти намеки не должны сделать решение легким, поэтому нам придется заниматься балансировкой.

Sudoku

Большинство людей думают, что Sudoku было придумано каким-то древним японцем, а это не так. Подобные головоломки можно увидеть в 19 веке во французских магазинах, известность им придал американский архитектор Howard Garns, который их модернизировал. Первый раз Sudoku было опубликовано в Соединенных Штатах в 1979 в журнале Dell.

3.2 Декларативный подход к дизайну

            Пользовательский интерфейс можно конструировать одним из двух методов: процедурнои декларативно. Процедурно, значит просто в коде. Например, когда вы программируете приложение Swing, вы пишите Java код для создания и манипулирования всеми объектами пользовательского интерфейса, такими как JFrame и JButton. Таким образом, Swing использует процедурный подход. Декларативный подход, не включает никакого кода. Когда вы конструируете простую веб-страницу, вы используете HTML, язык разметки основанный на XML, описывающий внешний вид страницы. HTML декларативен.
            Android пытается устранить зазор между процедурным и декларативными подходами, оставляя вам право выбора между этими подходами. Вы можете все сделать в коде Java, или вы можете сделать все в дескрипторах XML. Если вы посмотрите документацию на несколько компонент пользовательского интерфейса Android, вы увидите и Java APIs и декларативные атрибуты XML делающие одно и тоже.
            Что вы должны использовать? Любой способ приемлем, но мой совет будет такой: декларативный способ с XML, где только возможно. Код XML легче понять, чем соответствующий код Java, и будущие инструменты могут быть усовершенствованы для Android, например, конструкторы GUI и с ними проще работать.
Рисунок 3.1: Программа примера Sudoku для Android
            Теперь воспользуемся этим подходом для того чтобы создать начальный экран Sudoku.

3.3 Создание начального экрана

            Мы начнем с каркаса программы Android, который создает Eclipse plug-in. Тот самый, что вы сделали в разделе 1.2, Создание первой программы. Сделайте каркас, как для проекта «Hello, Android», но при этом введите следующие значения:
Project name: Sudoku
Package name: org.example.sudoku
Activity name: Sudoku
Application name: Sudoku
            В реальной программе, конечно, вы использовали бы ваши собственные имена. Имя пакета особенно важно. Каждое приложение в системе имеет уникальное имя пакета, поэтому выбираете оригинальное имя пакета.
            Я люблю держать окно эмулятора Android поверх всех остальных, всё временя и запускатьпрограмму после каждого изменения, в виду того что запуск самой программы занимает несколько секунд. Если вы проделайте необходимые шаги и запустите программу теперь, вы увидите пустой экран содержащий слова «Hello World, Sudoku.».        Первое что надо сделать для начала - изменить, чтобы в начальном экране для игры были кнопки, которые позволят игроку запустить новую игру, продолжать предыдущую, получить информацию об игре, или выйти из игры. Так что же нам придется изменить?
            Как обсуждено в главе 2, Ключевые концепции, программа для Android - есть совокупность activities, таких как пользовательский интерфейс. Когда вы создаете проект Sudoku, Android plug-in делает одну activities для вас внутри Sudoku.java:
            Android вызывает метод onCreate() вашей activities, который нужен для инициализации. Вызов setContentView( ) заполняет содержимое экрана activities виджетами Android. Мы могли бы использовать несколько строк кода Java, и возможно один или два класса для описания пользовательского интерфейса процедурном способом. Но вместо этого, plug-in предлагает декларативный путь, и мы будем его придерживаться.            В приведенном коде, R.layout.main отсылает с помощью идентификатора к файлу main.xml в директории res/layout (см. рисунок 3.2), main.xml объявляет пользовательский интерфейс в XML, поэтому нам нужно его доработать. Во время исполнения, Android парсит и инициализирует объявленные ресурсы там и компоненты, которые необходимы для текущей activities.
Рисунок 3.2: Первоначальные ресурсы в проекте Sudoku

            Важно заметить, что класс R управляется автоматически Android Eclipse plug-in. Когда вы создаете файл где-либо в директории res, plug-in замечает изменения и добавляет идентификатор ресурса внутрь R.java файла в директории gen. Если вы извлекаете или изменяете ресурс, R.java синхронизируется с этими изменениями. Если вы переместитесь в файл в редакторе, увидите что-то похожее:
Android resource manager использует шестнадцатеричные или просто целые для нагрузки реальных данных, строк, и других ресурсов, которые включены в ваш пакет. Вам не нужно потревожиться о их значениях. Просто имейте в виду, что они - handles, которые ссылаются на данные, а не объекты, которые содержат данные. Эти объекты не будут загрузаться до момента, когда они действительно необходимы. Заметьте, что почти каждая программа  в Android, включая Android framework имеют класс R. См. online документацию на android. Для всех built-in ресурсов вы можете использовать R[1].
            Так, теперь мы знаем, что необходимо доработать main.xml. Чтобы приступить к редактированию сделайте двойной щелчок по main.xml в Eclipse. В зависимости от как вы настроили Eclipse, вы увидите или визуальный layout-редактор или редактор XML. В используюемых автором версиях из ADT, визуальный layout-редактор неочень полезен, поэтому кликните по main.xml или выберете вкладку Source для просмотра XML.
            Первая строка main.xml выглядит следующим образом:
Все Android XML начинаются с этой строки. Она указывает compiler то, что
файл будет формой XML, в кодировке UTF-8.
            Затем мы увидим объявление :
layout - контейнер для одного или более child -объектов и режим их расположения на экране в пределах прямоугольника parent-объекта.
            Приведем перечень самых распространенных layouts, поддерживаемых Android:
  • FrameLayout: Расставляет своих детей так, чтобы все они распологались с левого верхнего угла экрана. Это используется для tabbed views и image switchers.
  • LinearLayout: Расставляет своих детей в однострочном или однорядном виде. Это самый частый layout, который вы будете использовать.
  • RelativeLayout: Расставляет своих детей в зависимости друг от друга. Это часто используется в формах.
  • TableLayout: Расставляет своих детей в строках и столбцах, подобных таблице HTML.
Некоторые параметры общие для всех layouts:
xmlns:android="http://schemas.android.com/apk/res/android". Объявление namespace XML для Android. Вы видите его первой строкой в XML файле.
android:layout_width="fill_parent", android:layout_height="fill_parent". Заполнить всю ширину и высоту родителя (в нашем случае, окно). Вероятные значения fill_parent и wrap_content.
Внутри тега , вы увидите один child widget:
Этот было объявление простого текста, изменим текст и добавим кнопки:

            Если вы видите предупреждения в редакторе о пропавших ограничениях по грамматики(Схемы DTD или XML), проигнорируйте их.
            Вместо трудного кодирования на английском в layout - файле, мы используем синтаксис @string/resid для того чтобы сделать ссылки на строки в файле res/values/strings.xml. Вы можете иметь другой вариант этого и других файлов ресурсов основанных на locale или других параметрах, таких как разрешение и ориентация экрана. Откройте файл теперь, переключитесь на вкладку strings.xml, если необходимо, и войдите это:
            Когда вы запустите программу, вы должны увидеть что-то похожее на Рисунок 3.3. Если результат не тот, то будут лишь косметические изменения.

Рисунок 3.3: Первый вариант экрана отверстия
            Давайте сделаем текст названия побольше и по центру, сделаем кнопки поуже и используем другие цвета для названия. Здесь объявление цвета, которое вы должны разместить в res/values/colors.xml:
 И новый layout:
             В этом варианте, мы вводим новый синтаксис, @+id/resid. Вместо ссылки на ресурс ID, объявленный где-нибудь в другом месте, вы создаете новый ресурс ID, к которому другие могут обратиться. Например, @+id/about_button - объявление идентификатора для для кнопки “О программе”, которую мы будем использовать далее. Результат показан на рисунке 3.4. Этот новый экран смотрится хорошо в режиме портрета (когда экран по высоте больше, чем по ширине), но как же режим ландшафта (когда экран широкий)? Пользователь может переключить режимы в любое время, например, выдвижением клавиатуры или поворотом телефона на бок, поэтому вам нужно предусмотреть это.
Рисунок 3.4: Экран отверстия с новым layout

Джо спрашивает: почему Android использует XML? Может это не эффективно?

            Android оптимизирован для мобильных устройств с ограниченной памятью и зарядом батареи, поэтому вы можете удивиться, что он использует XML. Ведь XML отличается ёмкостью и человекочитаемой формой? Хотя вы видите XML при написании вашей программы, Eclipse plug-in Android resource compiler, aapt, сжимает XML в бинарную форму. На устройстве не храниться текст XML.

3.4 Использование других ресурсов

            Для проверки, переключите эмулятор в режим ландшафта ( Ctrl+F11 или 7 или 9 ключ на keypad). Кнопка выхода не поместилась на экране (см. рисунок 3.5). Как исправить это?

Рисунок 3.5: В режиме ландшафта, мы не можем увидеть кнопку выхода.

            Вы можете попытаться отрегулировать layout так, чтобы контролы корректно работали с всеми ориентациями. К сожалению, это не всегда возможно. Тогда, вам нужно создать дополнительный layout для режима ландшафта. Этим мы и займемся.
            Создайте файл с именем res/layout-land/main.xml (заметьте: -land суффикс), содержащий следующий layout:
            
ВоспользуемсяTableLayout для создания 2-х колонок кнопок. Запустите программу снова (см. рисунок 3.6). Перейдите в режим ландшафта,- все кнопки видны. Вы можете использовать суффиксы для того, чтобы определить другие варианты любых ресурсов, не только layout, например, вы можете использовать их для того, чтобы обеспечить локализацию текстовых строк на разные языки. Каждый альтернативный файл ресурса должен иметь свой идентификатор.
Рисунок 3.6: Использование ландшафтоориентированного layout для отображения всех кнопок.
            Android поддерживает в настоящее время суффиксы для языка, региона, плотности пикселов, разрешения экрана, методов ввода, и других. См. документацию Android, для актуального перечня суффиксов и правил наследования[2].

Джо спрашивает: будут Dips и Sps?

            Исторически, программисты всегда конструировали пользовательский интерфейс в пикселах. Например, вы могли сделать поле шириной в 300, 5 пикселов пространства между колонки, и объявить иконки 16 пикселов на 16. Проблема, если вы запустите программу на новых экранах с большим разрешениям точек на дюйм(dpi), пользовательский интерфейс окажется более мелким. В худшем случае, он будет слишком трудным, для того чтобы с ним работать. Независимость от разрешений разрешает эту проблему.
            Android поддерживает все следующие единицы измерения:
  • px (pixels): Точки на экране.
  • in (inches): Размер как по линейке.
  • mm (millimeters): Размер как по линейке.
  • pt (points): 1/72 от дюйма.
  • dp        (density-independent pixels): Абстрактная мера, основанная на разрешении экрана. На экране с 160 точками на дюйм, 1dp = 1px.
  • dip: Синоним для dp, более часто используемый в примерах Google.
  • sp (scale-independent pixels): Подобный dp, но вычисляемый от маштаба размера шрифта пользователя.
            Для того чтобы ваш пользовательский интерфейс был масштабируем к любому в настоящим и будущим разрешениям экрана, я рекомендую вам всегда использовать размерность sp для размеров текста и размерность dip для всего остального. Вы должны также рассматривать использование векторной графики вместо bitmaps (см. главу 4, Исследование 2D -графики).

3.5 Создание About Box

            Когда пользователь нажимает кнопку About, не важно как, или касанием экрана(если устройство поддерживает данную технологию) или джойстиком D-pad или трекболом и отжимает кнопку, мы желаем вывести окно с некоторой информацией о Sudoku. После просмотра текста, пользователь может нажать кнопку назад и вернуться на начальный экран.
            Мы можем осуществить это несколькими способами:
  • Объявить новую Activity и запускать её.
  • Использовать AlertDialog и отображать его.
  • Использовать Subclass Android’s Dialog class и отображать его.
            Для этого примера, объявим новую activity. Подобно activity главного экрана Sudoku, activity “О программе” будет нужен файл layout. Мы назовем его res/layout/about.xml:
Нам нужна только одна версия этого layout, потому что он хорошо смотрится в обоих режимах: и портрета и ландшафта.
            Теперь добавьте строки для названий диалогового окна и текста(res/values/strings.xml): 
             Примечание: как ресурс, строки могут содержать просто HTML и иметь многострочность, символ слеш (\) внутри about_text предотвращает появление пробела раньше первого слова. Activity “О программе” должна быть объявлена внутри About.java. Все, что нужно для этого, так это перезагрузка onCreate( ) и вызов setContentView( ):
             Затем нам нужно связать её с кнопкой About в классе Sudoku. Начнем с импорта в Sudoku.java:
             В методе onCreate(), добавим код для вызова findViewById(), чтобы получить идентификатор кнопки и setOnClickListener(), чтобы указать Android объект для того, чтобы регистрировать касания или щелчок пользователя по кнопке:
             Пока мы здесь, мы проделаем тоже же для всех кнопок. Укажите константы(на вроде R.id.about_button, созданные Eclipse plug-in в R.java для кнопки о программе оно выглядит @+id/about_button в res/layout/main.xml).
            Код работает как приемник, поэтому классу Sudoku нужно обеспечить OnClickListener интерфейс и объявить вызнаваемый метод onClick[3]:
            Для запуска activity в Android, нам потребуется instance от the Intent   class. Есть 2 вида intents: public (named) intents зарегистрированные в системе и которые могут быть вызваны любым приложением и private      (anonymous) intents, которые используются в пределах одного приложения. Для этого примера, нам как раз нужен последний вид.
            Если вы запустите программу и нажмете на кнопку о программе теперь, то вы получите ошибку (см. рисунок 3.7). Так?
Рисунок 3.7: Ошибка
            Это из-за того что, мы забыли один важный момент: каждой activity необходимо объявление внутри AndroidManifest.xml. Сделайте двойной клик по нему, переключитесь
к режиму XML, если это необходимо на вкладке AndroidManifest.xml, и добавьте новый тег < activity > после тега закрытия первой activity:
             Теперь сохраните manifest и запустите программу снова, выбираете кнопку “О программе”, вы должны увидеть, что-то как рисуноке 3.8. Нажмите кнопку назад (Esc на эмуляторе). Если все хорошо, вы увидите начальный экран.
 Рисунок 3.8: Первый вариант экрана “О программе”

3.6 Применение тем

            Тема это коллекция стилей, которые изменяют внешний вид Android widgets. Темы были сделаны по аналогии с Cascading Style Sheets (CSS) для отделения содержимого веб-страниц от его представления. Android содержит несколько тем, к которым вы можете обращаться по имени[4],или вы можете создать свою собственную тему, изменяя существующую и заменяя принимаемые значения по умолчанию.
            Мы объявим собственную тему внутри res/values/styles.xml, но для этого примера мы будем использовать готовую. Раскройте редактором AndroidManifest.xml снова, и измените объявление About activity для добавления свойства тема:
Префикс @android: перед именем стиля Android, не последний, который будет ещё объявлен в вашей программе.
            Запустите программу снова, теперь экран “О программе” выглядит, как рисунке 3.9. Многих программы нуждаются в меню и опциях, поэтому следующие 2 раздела будут посвещены им.

Рисунок 3.9: Экран о программе после применения темы dialog box

3.7 Добавление меню

            Android поддерживает 2 вида меню. Во-первых, меню, которое вы получаете, когда вы нажимаете кнопку меню. Во-вторых, меню, которое появляется, когда вы нажимаете и держите ваш палец на экране (или нажали и держите центральную кнопку джойстика).
            Для начала давайте сделаем меню для одноименной кнопки, рисунок 3.10, сначало нам нужно объявить несколько строк, которые мы будем использовать:
 После этого мы объявим меню, используя XML в res/menu/menu.xml:
Рисунок 3.10: Нажатие кнопку Меню для того, чтобы раскрыть меню.
            Затем нам нужно доработать класс Sudoku для того, чтобы задействовать объявленное меню, нам нужно больше импортов:
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
После этого, мы переопределим метод Sudoku.onCreateOptionsMenu():
 getMenuInater() возвращает instance MenuInater, который мы используем для меню объявленного в XML и реального объекта.
            Событие onOptionsItemSelected() будет вызвано, когда пользователь выбирет любой подпункт меню. Для этого метода укажем:
             Settings - это класс, поэтому нам надо объявить его и его будет изменять пользователь.

Джо спрашивает: Почему не используйте HTML?

Android поддерживает вставку webbrowser напрямую в view через класс WebView (см. раздел 7.2, Веб и View). Так, почему не сделали так для экрана о программе? Фактически, вы могли сделать и так. WebView поддерживает более сложное форматирование, чем простой TextView, но он имеет некоторые ограничения (такие как невозможность прозрачного фона). Также, WebView более медленный и отъедает больше памяти, чем TextView. Для собственных приложений, используйте каким угодно способом.

3.8 Добавление Настроек

            Android обеспечивает хорошее средство для объявления установок вашей программы и их отображения не используя почти никакое кода. Вы объявляете установки в ресурсе представленном файлом res/xml/settings.xml:
             Программа Sudoku имеет 2 установки: одну для воспроизведения музыки и другую для показа подсказок. Ключами будут константные строки, которые будут использованы в базе данных настроек Android.
            Сделайте объявление класса Settings, и сделайте его наследником от PreferenceActivity:
             Метод addPreferencesFromResource() читает установки объявленные в XML и переносит их в views в текущей activity. Всю тяжелую работу осуществляет класс PreferenceActivity.     Не забудьте зарегистрировать Settings activity внутри AndroidManifest.xml:
             Теперь вернемся к Sudoku, нажмите меню, выберите установки… спустя некоторое время появиться экран (см. Рисунок 3.11). Попытайтесь изменить значения на нем и выйти из программы, и после этого запустите её снова, они все сохранились!

Рисунок 3.11: Настроек не много, которые нужны, но мы получили их легко.

Программирования чтения установок и манипулирование ими будет обсуждено в другой главе (главе 6, Локальное хранение данных). Теперь продвинемся дальше, к кнопке “Новая игра”.

3.9 Начало новой игры

            Если вы играли любые игры Sudoku, то вы знаете, что некоторые из них легки, а
некоторые безумно трудны. Когда пользователь выбирает новую игру, мы
выдаем диалоговое окно с вопросом об уровне сложности. Выбор из перечисления легко сделать в Android. Нам будут нужны ещё строки, добавим их в res/values/strings.xml.
 Создайте перечисление сложностей, как массив блока в res/values/arrays.xml:
 Нам будут нужны немного больше импортов в класс Sudoku:
Добавьте код со switch в метод onClick(), обрабатывающий щелчок по кнопке начала новой игры:
Метод openNewGameDialog() позаботится о создании диалога выбора уровня сложности из списка.
Метод setItems() принимает 2 параметра: resource ID листа перечисления и listener, который будет вызван когда один из пунктов будет выбран.
Когда вы запустите программ и нажмете кнопку новая игра, вы получите
диалоговое окно (рисунок 3.12).
Рисунок 3.12: Диалоговое окно выбора уровня сложности
            Мы фактически не запустили пока игру, но мы можем уже выбрать уровень сложности, а затем мы отображаем сообщения метода debug Log.d( ), происходящие в приложении.

3.10 Отладка с журналом сообщений

            Класс журнала содержит несколько статических методов печати сообщений, совпадающих с сообщениями в журнале системы Android:
  • Log.e( ): Ошибки
  • Log.w( ): Предупреждения
  • Log.i( ): Информация
  • Log.d( ): Debugging
  • Log.v( ): Verbose
            Пользователи никогда не будут видеть этот журнал, но вы как разработчик можете осмотреть его. В Eclipse, раскройте LogCat так: Window >Show View > Other... > Android > LogCat (см. рисунок 3.13). Он может быть отфильтрован по степени важности, или по тегам вызовов методов.
Рисунок 3.13: Выход Debugging в LogCat view
            Если вы не используете Eclipse, то вы можете увидеть такой же выход путем запуска команды logcat adb[5]. Я рекомендую вас вводить эту команду в отдельном окне и выводить полностью всю информацию с момента запуска эмулятора. Удобно выводить его на другой монитор,- оно не помешает работе.
            Я не могу описать полезность журнала Android во время разработки. Вспомните ту ошибку, которая была раньше с экраном о программе (Рисунок 3.7)? Если вы раскрыли LogCat тогда, то вы увидели бы это сообщение:
“ActivityNotFoundException: Unable to nd explicit activity class...have you declared this activity in your AndroidManifest.xml?” и стало бы все ясно сразу.

3.11 Отладка с отладчиком

            В дополнение к сообщениям журнала, вы можете использовать debugger Eclipse для того чтобы установить точки остановки, запустить пошаговую отладку, и для осмотра состояния вашей программы. Для начала подготовьте ваш проект для debugging путем добавления опции android:debuggable="true" в ваш файл AndroidManifest.xml:[6]
После этого, нажмите просто правой кнопкой по проекту, и выберите Debug As > Android Application.

3.12 Выход из игры

            Этой игре реально не нужна кнопка выхода, потому что пользователь может нажать на кнопку назад или на кнопку домашнего экрана, для того чтобы выйти. Но я хоту рассказать, для того чтобы показать вам, как прекратить activity. Добавьте в switch в методе onClick() :
            Когда кнопка “Выход” нажата, мы вызываем метод finish(). Он закрывает activity и возвращает управление к следующей activity на Android, находящейся на вершине стека приложений (обычно домашний экран).

3.13 Резюме

            Вот так, много всего задействовали в одной главе! Начали с наброска, потом изучили использование файлов layout, для того чтобы организовывать ваш интерфейс, использование ресурсов в Android для текстовой информации, цветов, и так далее. Вы научились добавлять управление: кнопки и текстовые поля, научились применять темы, для того чтобы изменять внешний вид программы, и даже создавать меню и настройки.
            Android сложная система, вы не обязаны знать все ее фишки сразу, когда вам нужна помощь, сотни страниц online-справки ответят на все вопросы по классам и методам, используемых тут[7].
В главе 4, Исследование 2D графики, мы будем использовать графические API Android, для того чтобы нарисовать плитки для игры Sudoku.


[1] http://d.android.com/reference/android/R.html
[2] http://d.android.com/guide/topics/resources/resources-i18n.html#AlternateResources

[3] Мы могли бы использовать анонимный внутренний класс для того чтобы отлавливать щелчки, но разработчики Android заявляют, каждый новый внутренний класс потребляет 1KB памяти.
[4] См. http://d.android.com/reference/android/R.style.html для символов начиная с «Theme_.»
[5] http://d.android.com/guide/developing/tools/adb.html
[6] Это происходит по умолчанию если вы используете эмулятор, но необходимо для отладке на реальном устройстве. Не забудьте только убрать при публикации релиза приложения.
[7] Для того чтобы смотреть online документацию, раскройте documentation.html от вашего Android SDK, или укажите вашему браузеру адрес http://d.android.com/guide/index.html.

Комментариев нет:

Отправить комментарий