Глава 1. Первое приложение Android так что пока вы можете не обращать внимания на их содержимое. Разные версии Android более подробно рассматриваются в главе 6. Рис. 1.3.Создание нового приложения Инструментарий Android обновляется несколько раз в год, поэтому окно мастера на вашем компьютере может несколько отличаться от моего. Обычно это не создает проблем; принимаемые решения остаются практически неизменными. (Если ваше окно не имеет ничего общего с изображенным, значит, инструментарий изменился более радикально. Без паники. Загляните на форум книги forums.bignerdranch.com, и мы поможем вам найти обновленную версию.) Щелкните на кнопке Next Во втором диалоговом окне снимите флажок Create custom launcher icon (рис. 1.4). При- ложение GeoQuiz будет использовать значок запуска по умолчанию. Проследите за тем, чтобы флажок Create activity был установлен.
Создание проекта Android 27 Рис. 1.4. Настройка параметров проекта Щелкните на кнопке Next В следующем диалоговом окне (рис. 1.5) вам предлагается выбрать вид создаваемой активности. Выберите в списке строку Blank Activity (пустая активность). Рис. 1.5. Создание новой активности
28 Глава 1. Первое приложение Android Щелкните на кнопке Next В последнем диалоговом окне мастера введите имя субкласса активности QuizAc- tivity (рис. 1.6). Обратите внимание на суффикс Activity в имени класса. Его присутствие не обязательно, но это очень полезное соглашение, которое стоит соблюдать. Рис. 1.6.Настройка новой активности Имя макета автоматически заменяется на activity_quiz в соответствии с переименова- нием активности. Имя макета записывается в порядке, обратном имени активности; в нем используются символы нижнего регистра, а слова разделяются символами подчеркивания. Эту схему формирования имен рекомендуется применять как для макетов, так и для других ресурсов, о которых вы узнаете позднее. Оставьте в списке Navigation Type значение None и щелкните на кнопке Finish . Среда Eclipse создает и открывает новый проект. Навигация в Eclipse Eclipse открывает ваш проект в инструментальном окне (workbench window), как показано на рис. 1.7. (Если вы только что установили Eclipse, закройте окно с при- ветствием Eclipse, чтобы открыть инструментальное окно.)
Построение макета пользовательского интерфейса 29 Слева находится панель Package Explorer . На ней выполняются операции с файлами, относящимися к вашему проекту. В середине находится панель редактора. Чтобы вам было проще приступить к ра- боте, Eclipse открывает в редакторе activity_quiz.xml В правой и нижней части инструментального окна также размещаются панели. Чтобы закрыть любую из панелей в правой части, щелкните на кнопке X рядом с именем панели (рис. 1.7). Панели в нижней части объединены в группу вкладок. Вместо того чтобы закрывать их, сверните всю группу при помощи кнопки в правом верхнем углу панели. Закрыть панели Свернуть группу вкладок Рис. 1.7. Освобождение места в инструментальном окне Когда вы сворачиваете панель, она закрепляется на полях инструментального окна Eclipse. Наведите указатель мыши на любой из маленьких значков на этих панелях инструментов, чтобы просмотреть имена свернутых панелей; щелкните кнопкой мыши, чтобы восстановить панель на экране. Построение макета пользовательского интерфейса По умолчанию Eclipse открывает activity_quiz.xml в конструкторе макетов Android, в котором отображается графическое представление макета. Этот режим бывает полезен, но сейчас мы будем работать в редакторе XML, чтобы вы лучше поняли, как работают макеты.
30 Глава 1. Первое приложение Android Чтобы перейти непосредственно к разметке XML, выберите вкладку activity_quiz.xml в нижней части редактора. В настоящее время файл activity_quiz.xml определяет разметку для активности по умолчанию. Такая разметка часто изменяется, но XML будет выглядеть примерно так, как показано в листинге 1.1. Листинг 1.1. Разметка для активности xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".QuizActivity" > android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="@string/hello_world" />
Прежде всего обратите внимание на то, что файл activity_quiz.xml не начинается со строки с объявлением версии и кодировки:
В версии ADT 21 эта строка не является обязательной в файлах макетов Android. Впрочем, она все еще достаточно часто встречается в файлах. Макет активности по умолчанию определяет два виджета (widgets): RelativeLayout и TextView Виджеты представляют собой структурные элементы, из которых составляется пользовательский интерфейс. Виджет может выводить текст или графику, взаимо- действовать с пользователем или размещать другие виджеты на экране. Кнопки, текстовые поля, флажки — все это разновидности виджетов. Android SDK включает множество виджетов, которые можно настраивать для полу- чения нужного оформления и поведения. Каждый виджет является экземпляром класса View или одного из его субклассов (например, TextView или Button ). На рис. 1.8 показано, как выглядят на экране виджеты RelativeLayout и TextView , определенные в листинге 1.1. Но это не виджеты, которые нам нужны. В интерфейсе QuizActivity задействованы пять виджетов: вертикальный виджет LinearLayout ; TextView ; горизонтальный виджет LinearLayout ; две кнопки Button На рис. 1.9 показано, как из этих виджетов образуется интерфейс QuizActivity.
Построение макета пользовательского интерфейса 31 Рис. 1.8. Виджеты по умолчанию на экране (вертикаль- ный) (горизон- тальный) Рис. 1.9. Запланированное расположение виджетов на экране
32 Глава 1. Первое приложение Android Теперь нужно определить эти виджеты activity_quiz.xml Внесите в файл activity_quiz.xml изменения, представленные в листинге 1.2. Раз- метка XML, которую нужно удалить, выделена перечеркиванием, а добавляемая разметка XML выделена жирным шрифтом. Эти обозначения будут использо- ваться в книге. Не беспокойтесь, если смысл вводимой разметки остается непонятным; скоро вы узнаете, как она работает. Но будьте внимательны: разметка макета не проверяется, и опечатки рано или поздно создадут проблемы. В зависимости от вашей версии инструментария в трех строках, начинающихся с android:text , могут быть обнаружены ошибки. Пока не обращайте внимания. мы их скоро исправим. Листинг 1.2. Определение виджетов в XML (activity_quiz.xml) xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".QuizActivity"> android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="@string/hello_world" />
Иерархия представлений 33 android:text="@string/false_button" />
Сравните XML с пользовательским интерфейсом, изображенным на рис. 1.9. Каж- дому виджету в разметке соответствует элемент XML. Имя элемента определяет тип виджета. Каждый элемент обладает набором атрибутов XML. Атрибуты можно рассматри- вать как инструкции по настройке виджетов. Чтобы лучше понять, как работают элементы и атрибуты, полезно взглянуть на разметку с точки зрения иерархии. Иерархия представлений Виджеты входят в иерархию объектов View , называемую иерархией представлений. На рис. 1.10 изображена иерархия виджетов для разметки XML из листинга 1.2. Рис. 1.10. Иерархия виджетов и атрибутов Корневым элементом иерархии представлений в этом макете является элемент Lin- earLayout . В нем должно быть указано пространство имен XML ресурсов Android https://schemas.android.com/apk/res/android
34 Глава 1. Первое приложение Android LinearLayout наследует от субкласса View с именем ViewGroup . Виджет ViewGroup предназначен для хранения и размещения других виджетов. Он используется в тех случаях, когда вы хотите выстроить виджеты в один столбец или строку. Другие субклассы ViewGroup — FrameLayout , TableLayout и RelativeLayout Если виджет содержится в ViewGroup , он называется потомком (child) ViewGroup Корневой элемент LinearLayout имеет двух потомков: TextView и другой элемент LinearLayout . У LinearLayout имеются два собственных потомка Button Атрибуты виджетов Рассмотрим некоторые атрибуты, используемые для настройки виджетов. android:layout_width и android:layout_height Атрибуты android:layout_width и android:layout_height , определяющие ширину и высоту, необходимы практически для всех разновидностей виджетов. Как правило, им задаются значения match_parent или wrap_content : match_parent — размеры представления определяются размерами родителя. wrap_content — размеры представления определяются размерами содержимого. (Иногда в разметке встречается значение fill_parent . Это устаревшее значение эквивалентно match_parent .) В корневом элементе LinearLayout атрибуты ширины и высоты равны match_parent Элемент LinearLayout является корневым, но у него все равно есть родитель — пред- ставление, которое предоставляет Android для размещения иерархии представлений вашего приложения. У других виджетов макета ширине и высоте задается значение wrap_content . На рис. 1.9 показано, как в этом случае определяются их размеры. Виджет TextView чуть больше содержащегося в нем текста из-за атрибута android:padding="24dp" . Этот атрибут приказывает виджету добавить заданный отступ вокруг содержимого при определении размера, чтобы текст вопроса не со- прикасался с кнопкой. (Интересуетесь, что это за единицы — dp ? Это пикселы, не зависящие от плотности (density-independent pixels), о которых будет рассказано в главе 8.) android:orientation Атрибут android:orientation двух виджетов LinearLayout определяет, как будут выстраиваться потомки — по вертикали или горизонтали. Корневой элемент Lin- earLayout имеет вертикальную ориентацию; у его потомка LinearLayout горизон- тальная ориентация. Порядок определения потомков определяет порядок их отображения на экране. В вертикальном элементе LinearLayout потомок, определенный первым, распола- гается выше остальных. В горизонтальном элементе LinearLayout первый потомок
Создание строковых ресурсов 35 является крайним левым. (Если только на устройстве не используется язык с пись- менностью справа налево — например, арабский или иврит; в этом случае первый потомок будет находиться в крайней правой позиции.) android:text Виджеты TextView и Button содержат атрибуты android:text . Этот атрибут сообщает виджету, какой текст должен отображаться Обратите внимание: значения атрибутов представляют собой не строковые лите- ралы, а ссылки на строковые ресурсы. Строковый ресурс — строка, находящаяся в отдельном файле XML, который на- зывается строковым файлом. Виджету можно назначить фиксированную строку (например, android:text="True" ), но обычно так делать не стоит. Лучше размещать строки в отдельном файле, а затем ссылаться на них. В главе 15 вы увидите, как строковые ресурсы упрощают локализацию. Строковые ресурсы, на которые мы ссылаемся в activity_quiz.xml , еще не существуют. Давайте исправим этот недостаток. Создание строковых ресурсов Каждый проект включает строковый файл по умолчанию с именем strings.xml Найдите на панели Package Explorer каталог res/values , раскройте его и откройте файл strings.xml Графический интерфейс нас пока не интересует; выберите вкладку strings.xml в ниж- ней части редактора. В шаблон уже включено несколько строковых ресурсов. Удалите неиспользуемую строку с именем hello_world и добавьте три новые строки для вашего макета. Листинг 1.3. Добавление строковых ресурсов (strings.xml)
GeoQuiz Hello, world! Constantinople is the largest city in Turkey. string> True False Settings
(Не удаляйте строку menu_settings — ваш проект содержит готовое меню. Удаление menu_settings вызовет каскадные ошибки в других файлах, относящихся к меню.) Теперь по ссылке @string/false_button в любом файле XML проекта GeoQuiz вы будете получать строковый литерал "False" на стадии выполнения.
36 Глава 1. Первое приложение Android Сохраните файл strings.xml . Если в файле activity_quiz.xml оставались ошибки, свя- занные с отсутствием строковых ресурсов, они должны исчезнуть. (Если ошибки остались, проверьте оба файла — возможно, где-то допущена опечатка.) Строковый файл по умолчанию называется strings.xml , но ему можно присвоить любое имя на ваше усмотрение. Проект может содержать несколько строковых файлов. Если файл находится в каталоге res/values/ , содержит корневой элемент resources и дочерние элементы string , ваши строки будут найдены и правильно использованы приложением. Предварительный просмотр макета Макет готов и его можно просмотреть в графическом конструкторе. Прежде всего убедитесь в том, что файлы сохранены и не содержат ошибок. Затем вер- нитесь к файлу activity_quiz.xml и выберите вкладку Graphical Layout в нижней части редактора. Рис. 1.11. Предварительный просмотр в графическом конструкторе макетов (activity_quiz.xml) От разметки XML к объектам View Как элементы XML в файле activity_quiz.xml превращаются в объекты View ? Ответ на этот вопрос начинается с класса QuizActivity
Ресурсы и идентификаторы ресурсов 37 При создании проекта GeoQuiz был автоматически создан субкласс Activity с име- нем QuizActivity . Файл класса QuizActivity находится в каталоге src (в котором хранится Java-код вашего проекта). На панели Package Explorer откройте каталог src , а затем содержимое пакета com. bignerdranch.android.geoquiz . Откройте файл QuizActivity.java и просмотрите его содер- жимое (листинг 1.4). Листинг 1.4. Файл класса QuizActivity по умолчанию (QuizActivity.java) package com.bignerdranch.android.geoquiz; import android.app.Activity; import android.os.Bundle; import android.view.Menu; public class QuizActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_quiz); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_quiz, menu); return true; } } (Если вы не видите все директивы import , щелкните на знаке ⊕ слева от первой директивы import , чтобы раскрыть список.) Файл содержит два метода Activity : onCreate(Bundle) и onCreateOptionsMenu(Menu) Пока не обращайте внимания на метод onCreateOptionsMenu(Menu) . Мы вернемся к теме меню в главе 16. Метод onCreate(Bundle) вызывается при создании экземпляра субкласса активно- сти. Такому классу нужен пользовательский интерфейс, которым он будет управ- лять. Чтобы предоставить классу активности его пользовательский интерфейс, следует вызвать следующий метод Activity : public void setContentView(int layoutResID) Этот метод заполняет (inflates) макет и выводит его на экран. При заполнении макета создаются экземпляры всех виджетов в файле макета с параметрами, опре- деляемыми его атрибутами. Чтобы указать, какой именно макет следует заполнить, вы передаете идентификатор ресурса макета. Ресурсы и идентификаторы ресурсов Макет представляет собой ресурс. Ресурсом называется часть приложения, которая не является кодом — графические файлы, аудиофайлы, файлы XML и т. д.
38 Глава 1. Первое приложение Android Ресурсы проекта находятся в подкаталоге каталога res . На панели Package Explorer видно, что файл activity_quiz.xml находится в каталоге res/layout/ . Строковый файл, содержащий строковые ресурсы, находится в res/values/ Для обращения к ресурсу в коде используется его идентификатор ресурса. Нашему макету назначен идентификатор ресурса R.layout.activity_quiz Чтобы просмотреть текущие идентификаторы ресурсов проекта GeoQuiz, откройте Package Explorer и раскройте содержимое каталога gen . Найдите и откройте файл R.java Поскольку этот файл генерируется процессом сборки Android, вам не следует его изменять, о чем деликатно предупреждает надпись в начале файла. Листинг 1.5. Текущие идентификаторы GeoQuiz /* AUTO-GENERATED FILE. DO NOT MODIFY. */ package com.bignerdranch.android.geoquiz; public final class R { public static final class attr { } public static final class drawable { public static final int ic_launcher=0x7f020000; } public static final class id { public static final int menu_settings=0x7f070003; } public static final class layout { public static final int activity_quiz=0x7f030000; } public static final class menu { public static final int activity_quiz=0x7f060000; } public static final class string { public static final int app_name=0x7f040000; public static final int false_button=0x7f040003; public static final int menu_settings=0x7f040006; public static final int question_text=0x7f040001; public static final int true_button=0x7f040002; } } Теперь понятно, откуда взялось имя R.layout.activity_quiz — это целочисленная константа с именем activity_quiz из внутреннего класса layout класса R Строкам также назначаются идентификаторы ресурсов. Мы еще не ссылались на строки в коде, но эти ссылки обычно выглядят так: setTitle(R.string.app_name); Android генерирует идентификатор ресурса для всего макета и для каждой строки, но не для отдельных виджетов из файла activity_quiz.xml . Не каждому виджету нужен
Ресурсы и идентификаторы ресурсов 39 идентификатор ресурса. В этой главе мы будем взаимодействовать только с двумя кнопками, поэтому идентификаторы ресурсов нужны только им. Чтобы сгенерировать идентификатор ресурса для виджета, включите в определение виджета атрибут android:id . В файле activity_quiz.xml добавьте атрибут android:id для каждой кнопки. Листинг 1.6. Добавление идентификаторов кнопок (activity_quiz.xml) ... > android:layout_height="wrap_content" android:padding="24dp" android:text="@string/question_text" /> android:layout_height="wrap_content" android:orientation="horizontal"> android:id="@+id/true_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/true_button" /> android:id="@+id/false_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/false_button" />
Обратите внимание: знак + присутствует в значениях android:id , но не в значени- ях android:text . Это связано с тем, что мы создаем идентификаторы, а на строки только ссылаемся. Сохраните файл activity_quiz.xml . Вернитесь к файлу R.java и убедитесь в том, что во внутреннем классе R.id добавились два новых идентификатора ресурсов. Листинг 1.7. Новые идентификаторы ресурсов (R.java) public final class R { public static final class id { public static final int false_button=0x7f070001; public static final int menu_settings=0x7f070002; public static final int true_button=0x7f070000; }