2. Разработка мобильных приложений под Android.

#### **ПОДГОТОВКА К РАЗРАБОТКЕ, ОСНОВНЫЕ ТЕРМИНЫ**

**Android** - бесплатная операционная система, основанная на Linux с интерфейсом программирования Java. Android поддерживает 2D и 3D-графику, используя библиотеки OpenGL, а также хранение данных в базе данных SQLite.

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

**Activity (Активность, Деятельность)** - представляет собой экран пользовательского интерфейса, который представлен классом Activity и разметкой в виде XML-файла (разметку можно создать и программно, но в настоящее время так не делают). Android-приложение способно состоять из нескольких форм (Activity) и может переключаться между ними во время выполнения приложения.

**Intents (Намерения)** - асинхронные сообщения, которые позволяют приложению запросить функции из других служб или действий. Приложение может делать прямые запросы службе или действию (явное намерение) или запросить у Android зарегистрированные службы и приложения (неявное намерение). Для примера, приложение может запросить через Intent приложение Контакты для получения данных или запустить браузер при нажатии ссылки в тексте вашего приложения.

**Views (Представление, Вид, Элемент управления, Компонент)** - пользовательский интерфейс. Это знакомые нам элементы управления: кнопки, текстовые поля, флажки, переключатели и т.п.

**Services (Службы)** - выполняют фоновые задачи без предоставления пользовательского интерфейса (например, проигрывание музыки). Они могут уведомлять пользователя через систему уведомлений Android.

**Content Provider (Контент-провайдер)** - предоставляет данные приложениям. С помощью контент-провайдера ваше приложение может обмениваться данными с другими приложениями.

**Broadcast Receiver (Приемник широковещательных сообщений/запросов)** - принимает системные сообщения и неявные намерения, может использоваться для реагирования на изменение состояния системы. Приложение может регистрироваться как приемник определенных событий и может быть запущено, если такое событие произойдет.

Для отладки приложений используется **эмулятор телефона** - виртуальная машина, на которой будет запускаться наше приложение. Также можно использовать и реальное устройство.

Чтобы создать эмулятор телефона, запускаем **AndroidStudio**, выбираем в меню **Tools**
мастера как на рис. 1.

   Нажимаем кнопку **Create a virtual device** и в новом окне видим набор возможных эмуляторов, рис. 2, в том числе и для часов. Скачайте необходимые эмуляторы. Для начала вполне подойдёт один эмулятор.

   Вводим, например, Android4. Выбираем нужную версию Android, размер экрана и т.д. Остальные настройки можно оставить без изменений. Нажимаем кнопку **OK**. Добавленные эмуляторы будут храниться в менеджере эмуляторов.

Окончательную версию приложения желательно проверять на настоящем устройстве. Начиная с Android 4.4, на устройстве (планшет, телефон, часы) нужно активировать режим разработчика. Для этого идём в Настройки, там открываем страницу "О телефоне" и щёлкаем семь раз на строчке с номером сборки **Build number**. После этого в настройках появится новый пункт **Для разработчиков** или что-то в этом роде, если производитель использует свою оболочку.

Открыв страницу для разработчиков, вам нужно включить отладку через USB. Также включаем опцию **Не выключать экран**.

#### **ПЕРВОЕ ПРИЛОЖЕНИЕ**

После установки Android Studio (далее Студия) можно приступать к созданию своей первой программы.

В качестве языка программирования для Android используется язык Java, с которым вы уже знакомы. Для создания пользовательского интерфейса используется **XML (англ. Расширяемый язык** разметки) **– структурированный язык, похожий на** HTML**. Как** вы убедитесь, работать с ним – не страшно, он очень простой.

По традиции каждый программист должен написать "Hello World!" (Здравствуй, Мир!) в качестве первой программы. Но программа "Hello World!" уже встроена в среду разработки в целях совместимости, а современные программисты (т.е. мы с вами) должны писать другую более сложную программу **Hello Kitty!**.

Поэтому разобьём задачу на две части. Сначала запустим готовую программу **Hello** World!**без** написания кода, чтобы убедиться, что весь инструментарий корректно установился, и мы можем создавать и отлаживать программы. А потом уже напишем свою первую программу.

Запускаем Студию и выбираем **File** | New | New Project...**. Появится диалоговое окно мастера, рис. 3.**

Поле **Application name:** - понятное имя для приложения, которое будет отображаться в заголовке приложения. По умолчанию у вас уже может быть **My Application**. Заменим на **Hello** World**.**

Поле **Company Domain:** служит для указания вашего сайта. По умолчанию там может появиться ваше имя как пользователя компьютера. Если сайт у вас есть, то можете ввести его адрес, либо придумайте какое-нибудь название. Введённое имя запоминается и будет автоматически подставляться в следующих новых проектах.

Поле **Package name:** формирует специальный Java-пакет на основе вашего имени из предыдущего поля.

Третье поле **Project location:** позволяет выбрать место на диске для создаваемого проекта.

   Нажимаем на кнопку **Next** и переходим к следующему окну. Здесь мы выбираем типы устройств, под которые

будем разрабатывать своё приложение. В большинстве случае мы будет писать для
смартфонов и планшетов, поэтому оставляем флажок у первого пункта, рис. 4.

Также вы можете писать приложения для Android TV, Android Wear и Glass.

    Кроме выбора типа устройств, надо выбрать минимальную версию системы, под которую будет работать приложение. Выберите **API 16**.

Идём дальше и снова нажимаем кнопку **Next**. Здесь следует выбрать внешний вид экрана приложения.

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

Пару лет назад был только один шаблон. Список шаблонов постоянно пополняется и теперь их больше, рис. 5:

* Blank Activity
* Empty Activity
* Fullscreen Activity
* Google Maps Activity
* Google AdMod Ads Activity
* Login Activity
* Master/Detail Flow
* Navigation Drawer Activity (новинка)
* Scrolling Activity (новинка)

Шаблон **Empty Activity** предназначен для обычных телефонов. На картинке над названием шаблона вы видите приблизительный вид приложения с использованием данной заготовки. Для нас подойдёт этот вариант. Практически все примеры на сайте написаны с помощью данного шаблона.

Шаблон **Master/Detail Flow** предназначен для планшетов с реализацией двухпанельного режима. Шаблон **Fullscreen Activity** можно использовать для игр, когда требуется дополнительное пространство без лишних деталей. Другие шаблоны нужны для создания приложений с гуглокартами или сервисами Google Play.

Итак, мы выбрали вариант **Empty Activity** и переходим к следующему окну. Мы закончили с первоначальной настройкой.

Нажимаем кнопку **Finish**, при создании нового проекта, появится следующее диалоговое окно, рис 6.

    А дальше студия формирует проект и создаёт необходимую структуру из различных файлов и папок. Поначалу глаза разбегаются. Давайте разбираться. Боковая левая часть студии имеет несколько вертикальных вкладок. Скорее всего у вас будет активна первая вкладка **1:Project**, рис. 7. Вкладки **Structure** и **Captures** используются гораздо реже.

    В левой части среды разработки на вкладке **Android** появится иерархический список из папок, которые относятся к проекту. В некоторых случаях желательно переключиться на режим **Project**, который показывает истинное расположение файлов. Но на первых порах удобнее использовать именно вид **Android**, который прячет служебные файлы, чтобы не путать новичков.

Вкладка **Android** содержит две основные папки: **app** и **Gradle** Scripts**. Первая папка**app**содержит** все необходимые файлы приложения - код, ресурсы картинок и т.п. Вторая папка служит для различных настроек, управления проектом и многих других вещей.

Сейчас нас должна интересовать папка **app**. Раскройте её.

В ней находятся три папки: **manifest**, **java**, **res**, рис. 8.

* **manifest**

Папка **manifest** содержит
единственный файл манифеста **AndroidManifest.xml**, рис. 9. В этом файле должны быть
объявлены все активности, службы, приёмники и контент-провайдеры приложения.
Также он должен содержать требуемые приложению разрешения. Например, если
приложению требуется доступ к сети, это должно быть определено здесь.

"AndroidManifest.xml" можно рассматривать, как описание для развертывания Android-приложения.

* **java**

Папка **java** содержит две подпапки - рабочую и для тестов. Рабочая папка имеет название вашего пакета и содержит файлы классов. Сейчас там один класс **MainActivity**. Папку для тестов можете не трогать.

* **res**

Папка **res** содержит файлы ресурсов, разбитых на отдельные подпапки, рис. 10.

* **drawable** - в этих папках хранят графические ресурсы - картинки и xml-файлы, описывающие цвет и фигуры.
* **layout** - в данной папке содержатся xml-файлы, описывающие внешний вид форм и различных элементов форм. После создания проекта там уже имеется файлactivity\_main.xml, который отвечает за внешний вид главного окна приложения.
* **mipmap** - здесь хранят значки приложения под разные разрешения экрана

* **values** - тут у нас располагаются какие-либо строковые ресурсы, ресурсы цветов, тем, стилей и измерений, которые мы можем использовать в нашем проекте. Здесь вы можете видеть папки colors.xml, dimens.xml, strings.xml, styles.xml.

Со временем вы будете свободно ориентироваться в этих папках.

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

Для изучения вам нужно
открыть два файла - **MainActivity** (скорее всего он уже
открыт) и **activity\_main.xml** (**res/layout**) в
центральной части Студии, рис. 11. Если файлы не открыты, то откройте их

самостоятельно двойным щелчком для редактирования (или просмотра). Таким способом вы можете открыть любой нужный вам файл.

    Не будем пока изучать код, а просто нажмём на зелёный треугольник **Run** (Shift+F10) на панели инструментов в верхней части студии для запуска приложения.

Если всё сделали правильно (вообще для правильного запуска эмулятора необходимо правильно настроить его, правильно установить пакеты для запуска эмулятора (для каждой комплектации ПК они могут быть свои)), то в
эмуляторе загрузится ваша программа. Поздравляю!

Итак, если программа загрузилась в эмуляторе, то увидите окно приложения с надписью **Hello World!**, рис. 12. Заголовок у программы будет также **Hello World**. Все эти строки можно найти в файле **res/values/strings.xml** и отредактировать при желании.

    Теперь посмотрим на код. Сначала изучим **activity\_main.xml**.

Смотреть его можно в двух режимах - **Design** и **Text**. Откройте в режиме **Text**.

```
<RelativeLayout

```

```
xmlns:android="http://schemas.android.com/apk/res/android

```

```
xmlns:tools="http://schemas.android.com/tools"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="match_parent"

```

```
android:paddingLeft="@dimen/activity_horizontal_margin"

```

```
android:paddingRight="@dimen/activity_horizontal_margin"

```

```
android:paddingTop="@dimen/activity_vertical_margin"

```

```
android:paddingBottom="@dimen/activity_vertical_margin"

```

```
tools:context=".MainActivity">

```

```
<ТextView

```

```
android:text="@string/hello_world"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content" />

```

```
</RelativeLayout>

```

Немного о XML-коде. Имеется специальный контейнер **RelativeLayout**, в котором размещён компонент **TextView**, предназначенный для вывода текста.

Теперь посмотрим на Java-код (MainActivity.java)

```
package ru.schoolbreaker.helloworld;

```

```
import android.support.v7.app.AppCompatActivity;

```

```
import android.os.Bundle;

```

```
public class MainActivity extends AppCompatActivity {

```

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_main);}}

```

Перед вами открыт файл класса, где имя класса **MainActivity** совпадает с именем файла с расширением **java** (это правило, установленное языком Java). В первой строке идет название пакета - его мы задавали при создании проекта (Package Name). Далее идут строки импорта необходимых классов для проекта. Для экономии места они свёрнуты в одну группу. Разверните её. Если имена классов выводятся серым цветом, значит они не
используются в проекте (подсказка **Unused import statement**) и можете спокойно удалить лишние строки.

Далее идёт объявление самого класса, который наследуется (extends) от абстрактного класса **Activity**. Это базовый класс для всех экранов приложения. Не исключено, что у вас будет **AppCompatActivity**, если при создании проекта вы оставили поддержку старых устройств. Класс **AppCompatActivity** и относится к библиотеке совместимости. У неё есть все нужные методы и вспомогательные классы, но названия могут различаться. И смешивать названия нельзя. Если используете класс из библиотеки совместимости, то методы берите соответствующие.

В самом классе мы видим метод **onCreate()** – он вызывается, когда приложение создаёт и отображает разметку активности. Метод
помечен как **protected** и сопровождается аннотацией **@Override** (переопределён из базового класса). Разберём код метода.

Строка **super.onCreate(savedInstanceState);** –
это конструктор родительского класса, выполняющий необходимые операции для
работы активности. Эту строчку вам не придётся трогать, оставляйте без
изменений.

Вторая строчка **setContentView(R.layout.activity\_main);** представляет больший
интерес. Метод **setContentView(int)** подключает содержимое из
файла разметки. В качестве аргумента мы указываем имя файла без расширения из
папки **res/layout**. По умолчанию проект создаёт в нём файл **activity\_main.xml**. Вы можете
переименовать файл или создать свой файл с именем **cat.xml** и подключить его к своей
активности. Тогда код будет выглядеть так:

```
setContentView(R.layout.cat);

```

Настало время создать программу "Hello Kitty!". На
данный момент наша программа слишком проста. Представьте себе, что у вас на
экране должны располагаться несколько кнопок, текстовых полей, картинок.
Каждому объекту нужно задать размеры, координаты, цвет, текст и так далее.
Android поддерживает способ, основанный на XML-разметке, который будет
напоминать разметку веб-страницы. Начинающие программисты могут использовать
визуальный способ перетаскивания объектов с помощью мыши. Более продвинутые
могут писать код вручную. Чаше используется комбинированный подход.

Файлы XML-разметки находятся в папке **res/layout** вашего
проекта. Слово "res" является сокращением от слова
"resources" (ресурсы). Папка содержит ресурсы, не связанные с кодом.
Кроме разметки, там же содержатся изображения, звуки, строки для локализации и
т.д.

Раскройте слева в структуре проектов папки **res/layout** и дважды щелкните на файле **activity\_main.xml**, если он у вас закрыт. Обратите внимание, что XML-файлы можно просматривать в двух режимах: текстовом и визуальном. Для этого предназначены две вкладки в нижней части окна редактора: **Design** и **Text**, рис. 13.

    Переключитесь в режим **Text**.

Структура XML-файла достаточна проста - стандартное дерево XML-элементов, где каждый узел является именем класса **View** (TextView - один из элементов View). Вы можете создать интерфейс программы, используя структуру и синтаксис XML. Подобный подход позволяет разделить код программы и визуальное представление.

Можно продолжить работу над открытым проектом **Hello World** и модифицировать её под новые задачи. Для приобретения твёрдых навыков можете снова создать новый проект, не подглядывая в описание. Назовите проект **Hello Kitty** и повторите все предыдущие шаги.

Когда разметка открыта в графическом представлении, то слева от основной части редактора кода можно увидеть панель инструментов, в которой сгруппированы различные элементы по группам **Layouts**, **Widgets**, **Text Fields** и так далее. В группе **Widgets** найдите элемент **ImageButton**, перетащите её на форму и отпустите, рис. 14. Точное расположение нас не интересует, постарайтесь разместить компонент в центре экрана активности.

    Теперь научимся менять фон для экрана приложения. Сейчас у нас экран белого цвета. Подготовим цветовой ресурс с нужным цветом.
Возвращаемся в файл разметки **activity\_main.xml**. Справа найдите вкладку **Properties**, в которой отображаются свойства для выбранного элемента. Новички часто путаются на первых порах и начинают менять свойства не у тех элементов, которые им были нужны. Сейчас у вас есть сама форма, графическая кнопка **ImageButton** и текстовая метка **TextView** с надписью **Hello** World!**. Пощёлкайте по этим элементам, чтобы увидеть, как меняется содержание** свойств в панели свойств. Так как мы собираемся работать с фоном экрана приложения, то щёлкните в области формы и в панели свойств найдите свойство **background**. Щелкните рядом с этим словом во второй колонке, где нужно прописывать значения, рис. 15. Появится текстовое поле, в которое можно ввести значение вручную, и кнопка с тремя точками, которая запустит диалоговое окно для создания ресурса, рис. 16.

    Переходим на вкладку **Color**, рис. 17. и выбираем цвет, удобным вам способом. Например, вводим значение **FFC0CB** в текстовом поле с меткой **#**.

    Нажимаем кнопку **OK** и форма окрасится в розовый цвет.

Если переключиться в текстовый режим, то увидим, что у элемента **RelativeLayout** добавилась строчка:

```
android:background="#ffffc0cb"

```

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

Снова нажимаем на кнопку с тремя точками у свойства **background** и в диалоговом окне переходим на вкладку **Project**. Нажимаем на кнопку **New Resource...** и выбираем вариант **New Color Value...**. Появится ещё одно диалоговое окно. Заполняем пустые поля нужными значениями. Обратите внимание, что ресурс будет сохранён в новом файле **colors.xml**. Это общепринятый стандарт для хранения цветов, хотя вы можете сохранить ресурс и в файле **strings.xml** и **styles.xml**, которые можно выбрать из выпадающего списка, рис. 18.

Нажимаем **OK** и смотрим на изменения. Теперь цвет задан следующим образом:

```
android:background="@color/background_color"

```

Далее поменяем картинку для графической кнопки. Находим
подходящее изображение и копируем его, например, в папку **res/drawable-hdpi**
(res/drawable)**. Можно в Проводнике скопировать нужную картинку и,**
переключившись на студию, вставить её в нужную папку студии.

Когда вы поместите графический файл в указанную папку, то студия автоматически создаёт ресурс типа **Drawable** с именем файла без расширения, к которому можно обращаться программно. Выделяем элемент **ImageButton** на форме и в панели свойств выбираем свойство **src**. Снова щёлкаем на кнопке с тремя кнопками и выбираем ресурс в категории **Drawable** - вы там должны увидеть ресурс *kitty* (имя добавленного ранее файла), рис. 19.

    Запомните: имена ресурсов должны начинаться с буквы и могут содержать буквы и цифры, а также знак нижнего подчеркивания. Другие символы типа тире, решётки и т.д. использовать нельзя.
Там же в окне свойств находим свойство **onClick** и вручную прописываем **onClick** - это будет именем метода для обработки нажатия на кнопку, рис. 20.

    Напоследок, выделите элемент **TextView** с надписью **Hello, World**ив окне свойств посмотрите на его идентификатор (ID). Если там пусто, то удалите его, он не оправдал наших надежд, рис. 21.

    Мы закончили работу с графическим интерфейсом приложения, рис. 22.

В категории **Widgets** найдите компонент **Large Text**иперетащите его на форму приложения. Постарайтесь разместить его под графической кнопкой с котёнком.

У этого компонента точно будет что-то написано в свойстве **id**. Скорее всего, это будет **@+id/textView**. Запомните его.

Должно получиться следующее:

```
<RelаtiveLayout xmlns:andrоid="http://schemas.android.com/apk/res/android"

```

```
xmlns:tools="http://schemas.android.com/tools"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="match_parent"

```

```
android:backgr

```

```
android:paddingBottom="@dimen/activity_vertical_margin"

```

```
android:paddingLeft="@dimen/activity_horizontal_margin"

```

```
android:paddingRight="@dimen/activity_horizontal_margin"

```

```
android:paddingTop="@dimen/activity_vertical_margin"

```

```
tools:context="ru.schoolbreaker.hellokitty.MainActivity">

```

```
<ImаgeButton

```

```
android:id="@+id/imageButton"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:layout_centerHorizontal="true"

```

```
android:layout_centerVertical="true"

```

```
android:onClick="onClick"

```

```
android:src="@drawable/pinkhellokitty" />

```

```
<ТextView

```

```
android:id="@+id/textView"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:layout_alignLeft="@+id/imageButton"

```

```
android:layout_alignStart="@+id/imageButton"

```

```
android:layout_below="@+id/imageButton"

```

```
android:layout_marginTop="42dp"

```

```
android:text="Large Text"

```

```
android:textAppearance="?android:attr/textAppearanceLarge" />

```

```
</RelativeLayout>

```

Установите курсор мыши внутри текста **"onClick"** у кнопки и нажмите комбинацию **Alt**+**Enter.** В всплывающем окне выберите вариант **Create 'onClick(View)' in 'MainActivity'**, рис. 23.

В коде класса **MainActivity** появится заготовка для обработки щелчка кнопки.

Теперь открыт файл **MainActivity.java**, продолжим работу в нём. Так как мы собираемся менять текст в текстовой метке, необходимо прописать данный элемент в коде. До метода **onCreate()** наберите строчку:

```
private TextView mHelloTextView;

```

Мы объявили переменную типа **TextView** под
именем **mHelloTextView**.

Если вы набирали вручную и при подсказках использовали
клавишу Enter, то класс **TextView** автоматически
импортируется и запись появится в секции **import**. Иначе студия
подчеркнёт название класса **TextView** и
предложит импортировать его вручную. Добавится строчка:

```
import android.widget.TextView;

```

Далее внутри метода **onCreate()** после
вызова метода **setContentView()** добавьте
строку:

```
mHelloTextView = (TextView)findViewById(R.id.textView);

```

```
/*помните, мы просили запомнить идентификатор? */

```

Пишите и активно используйте автозавершение (**Ctrl+Пробел**) при наборе слов. Студия
часто помогает подсказками. Теперь система знает о существовании элемента **TextView**,
и мы можем к нему обращаться для изменения различных свойств, например, поменять текст. Переходим к заготовке для щелчка кнопки.

```
public void onClick(View view) {}

```

В следующих занятиях мы подробнее разберём работу с данным методом, пока просто пишем код между фигурными скобками:

```
mHelloTextView.setText("Hello Kitty!");

```

Мы обращаемся к элементу **mHelloTextView** и
через его метод **setText()** программно
меняем текст на нужные слова.

Запускаем программу и нажимаем на кнопку с изображением
котёнка. Если всё сделали правильно, то отобразится замечательная фраза, рис.
24. С этого момента можете считать себя настоящим программистом - вы научились
создавать цветовые и графические ресурсы, менять фон у приложения через

XML-разметку, обрабатывать нажатия кнопки и выводить текстовые сообщения.

Исходный код нашего приложения должен выглядеть так:

```
package ru.schoolbreaker.hellokitty;

```

```
import android.os.Bundle;

```

```
import android.support.v7.app.AppCompatActivity;

```

```
import android.view.View;

```

```
import android.widget.EditText;

```

```
import android.widget.TextView;

```

```
public class MainActivity extends AppCompatActivity {

```

```
private TextView mHelloTextView;

```

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_main);

```

```
mHelloTextView = (TextView) findViewById(R.id.textView);}

```

```
public void onClick(View view) {

```

```
mHelloTextView.setText("Hello Kitty!");} }

```

Программа получилась замечательная, но у неё есть
недостаток. Она показывает одну и ту же фразу "Hello Kitty!". Сделаем
так, чтобы программа приветствовала вас по имени. Найдите в разделе **Text**
Fields**компонент**Plain
Text**и**
перетащите его на экран активности, разместив где-то над картинкой. Оставляем
все свойства без изменений, разве только в свойстве **hint** можно
добавить строчку-подсказку, которая будет исчезать при вводе текста.

```
<EditТext

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:id="@+id/editText"

```

```
android:layout_above="@+id/imageButton"

```

```
android:layout_centerHorizontal="true"

```

```
android:layout_marginBottom="66dp"

```

```
android:hint="Введите имя кота" />

```

Переходим в класс **MainActivity** и
добавляем новую переменную рядом с переменной **mHelloTextView**:

```
private EditText mNameEditText;

```

Свяжем созданную переменную с компонентом в методе **onCreate()**:

```
mNameEditText = (EditText) findViewById(R.id.editText);

```

Поменяем код для щелчка кнопки.

```
public void onClick(View view) {

```

```
if (mNameEditText.getText().length() == 0) {

```

```
mHelloTextView.setText("Hello Kitty!");

```

```
} else {

```

```
mHelloTextView.setText("Привет, " + mNameEditText.getText());} }

```

Мы внесли небольшую проверку. Если в текстовом поле пустой текст, то длина текста составляет ноль символов, и мы по-прежнему выводим надпись "Hello Kitty!". Если пользователь введёт имя, то приложение поздоровается с ним. Какая умная и вежливая программа у нас получилась, рис. 25.

#### **РАБОТА С КНОПКАМИ**

Мы немного поработали с кнопкой. Теперь рассмотрим работу с
обработкой щелчков подробнее.

Можете использовать старый проект или создать новый проект
со стандартными настройками.

В прошлый раз мы использовали **ImageButton**.
Сейчас будем работать с обычной кнопкой. В режиме **Design** подведите
курсор к элементу **Button** и
перетащите его на форму. В результате ваших действий на форме появится стандартная
кнопка с настройками по умолчанию. В правой части экрана у вас имеется окно **Properties**,
в котором вы можете настроить необходимые свойства для кнопки. Присваиваем
свойству **id** новое
значение **buttonHello** вместо
стандартного **button**.
Если временно переключиться в режим **Text**, то увидите, что на самом деле
атрибут имеет значение **@+id/buttonHello**. Естественно, если вы
редактируете свойства в текстовом виде, то вам тоже надо придерживаться этого
стандарта. Свойству **Text** присвойте текст **Поздороваться**.

Если вы открыли предыдущий проект, то там уже был компонент **TextView** с
текстом **Hello,**
World**(если**
вы создали новый проект). Компонент **TextView** является
текстовой меткой для вывода текста, который нельзя редактировать. В метке будем
выводить приветствие после щелчка кнопки. В окне свойств удаляем текст из
свойства **Text**,
чтобы в текстовой метке ничего не было. Проследите, чтобы у него был
идентификатор. Если в окне свойств ничего нет, то добавьте свой идентификатор,
например, **textView** (**@+id/textView**).

Если вам не нравится взаимное расположение элементов, то
можете на форме перетаскивать элементы, меняя их местами.

Будем считать, что интерфейс программы готов - у нас есть
кнопка для нажатия и текстовая метка для вывода сообщений.

Теперь нужно научиться писать код для щелчка кнопки.
Переключитесь с режима **Design** на режим **Text** и
найдите тег **<Button>**.
Добавьте к нему еще одну строчку:

```
<Вutton

```

```
   android:id="@+id/buttonHello"

```

```
   android:layout_width="wrap_content"

```

```
   android:layout_height="wrap_content"

```

```
   android:layout_alignParentLeft="true"

```

```
   android:layout_alignParentTop="true"

```

```
   **android:onClick="onClick"**

```

```
   android:text="Поздороваться" />

```

```
public void onClick(Viеw viеw) {}

```

На самом деле это можно было сделать и через графическую
модель. Переключитесь обратно в режим **Design** и
найдите в свойствах кнопки пункт **OnСlick**. Там будет
прописано имя метода, которое мы задали через XML. Пользуйтесь любым удобным
вам способом.

Таким образом мы определили событие **onClick** для
кнопки (щелчок) и теперь осталось написать обработчик события. Вы уже знаете,
как в Android Studio быстро создать заготовку. В текстовом режиме нажимаем
комбинацию **Alt+Enter** и
шаблон готов.

```
public void оnClick(View view) {  }

```

Вставим код.

```
public void onClick(View view){

```

```
TextView helloTextView = (TextView)findViewById(R.id.textView);

```

```
helloTextView.setText("Hello Kitty!");}

```

Впрочем, это код нам уже знаком, но есть небольшое отличие.
Текстовую метку мы объявляем и присваиваем ссылку на нужный компонент сразу в
методе щелчка. Приложение у нас простое и доступ к текстовой метке больше нигде
не осуществляется. Поэтому нет нужды объявлять переменную на уровне класса и
инициализировать её в методе **onCreate()**. Если вы
набирали текст самостоятельно, то у вас всё получится.

Запускаем проект и нажимаем на кнопку, чтобы увидеть результат. Можете изменить текст по своему желанию, например, на *Ты* кто такой? Давай, до свидания!*, но это сообщение будет выглядеть мягко* говоря, странным.

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

```
package ru.schoolbreaker.hellokitty;

```

```
import android.app.Activity;

```

```
import android.os.Bundle;

```

```
import android.view.Menu;

```

```
import android.view.MenuItem;

```

```
import android.view.View;

```

```
import android.widget.TextView;

```

```
public class MainActivity extends Activity {

```

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_main);}

```

```
public void onClick(View view){

```

```
TextView helloTextView = (TextView)findViewById(R.id.textView);

```

```
helloTextView.setText("Hello Kitty!");}}

```

Ещё раз обратите внимание на строчку **TextView helloTextView = (TextView)findViewById(R.id.textView);**. Метод **findViewById()** дословнопереводится с английского как **НайдиКомпонентПоИдентификатору** и применяет тот же подход - вы указываете в параметре идентификатор ресурса и программа ищет подходящий компонент. В дальнейшем вы постоянно будете использовать данный код в своих программах. Интерфейс будет выглядеть следующим образом, рис. 26.

    Новички на первых порах часто делают элементарную ошибку - помещают строчку инициализации объекта до метода **setContentView()**.

Запомните, сначала нужно вывести шаблон (**R.layout.activity\_main**), а
только потом кнопки, текстовые поля, переключатели и т.д. В нашем случае
используется отдельный метод для кнопки, который формируется после метода **setContentView()**.
Позже, в других проектах вы увидите стандартные приёмы инициализации объектов.

На самом деле, способ обработки щелчка кнопки, который мы
применили, является относительно новым для разработчиков. Google рекомендует
использовать данный способ как удобный, требующий меньше кода и понятный для
чтения. Но тем не менее вам придётся сталкиваться со старым способом. Поэтому
необходимо изучить и второй способ, чтобы понимать другие примеры.

Добавьте в проект новую кнопку. Если первую кнопку мы
добавляли перетаскиванием элемента **Button** с
панели инструментов, то на этот раз мы попробуем создать кнопку вручную.
Откройте файл **activity\_main.xml** в
режиме редактора кода и скопируйте код для первой кнопки. Потом с новой строки
вставьте скопированный текст и подправьте несколько атрибутов, например, так.

```
<Вutton

```

```
   android:id="@+id/buttonCrowsCounter"

```

```
   android:layout_width="wrap_content"

```

```
   android:layout_height="wrap_content"

```

```
   android:layout_alignParentLeft="true"

```

```
   android:layout_below="@+id/buttonHello"

```

```
   android:text="Считаем ворон" />

```

```
private Button mCrowsCounterВutton;

```

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

Снова открываем файл **MainActivity.java** и
пишем следующий код сразу после объявления класса и до метода **onCreate()**:

```
private Вutton mCrowsCounterButton;

```

Во время набора студия будет пытаться угадать, что вы хотите
ввести. Активно используйте подсказки. Например, уже при первом вводе символа **B** студия
предложит несколько вариантов на эту букву. Если нужное слово находится первым
в списке, то нажимайте клавишу Enter, иначе выберите нужное слово из списка и
потом уже нажимайте Enter. Это удобно при наборе длинных имён классов и
переменных.

Google разработал целое руководство по наименованию
переменных. Например, закрытая переменная на уровне класса должна начинаться с
символа **m** (member),
а далее идёт понятное название с заглавной буквы. Давайте попробуем
придерживаться этого стиля.

После строчки **setContentView(R.layout.activity\_main);** пишем:

```
mCrowsCounterButton = (Button)findViewById(R.id.buttonCrowsCounter);

```

Переходим к самому важному - обработчику щелчка кнопки. Нам
понадобится дополнительная переменная-счётчик **mCount**, которая будет
содержать число подсчитанных ворон (её необходимо разместить выше метода **onCreate()** рядом
с переменной **mButtonCrowsCounter**).

```
private int mCount = 0;

```

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

Как использовать автодополнение. Сначала вводим первые
символы слова **mCrowsCounterButton** (можно
маленькими буквами) и нажимаем Enter, если видим, что нужная подсказка
появилась. После этого слова ставим точку и должны появиться опять подсказки,
которые относятся к данной переменной. Начинаем вводим первые буквы слова **setOnClickListener**.
Здесь тоже проблем обычно не возникает. На данный момент у нас получилась
строка **mCrowsCounterButton.setOnClickListener();**.
Ставим курсор внутри круглых скобок и набираем **new**
OnClickListener**. Здесь важно набрать символ**O**в**
верхнем регистре. Тогда у вас появится нужная подсказка типа **OnClickListener{...}**
(android.view.View)**. Нажимаем Enter и получаем нужную заготовку, внутри**
которой вставляем код:

```
mCrowsCounterButton.setOnClickListener(new View.OnClickListener() {

```

```
@Override

```

```
public void onClick(View v) {

```

```
mInfoTextView.setText("Я насчитал " + ++mCount + " ворон");}});

```

Ранее мы объявили переменную **helloTextView** внутри
метода **onClick()**,
из которого она недоступна в других методах. Поэтому поступим с ней так же, как
с кнопкой - объявим текстовую метку на уровне класса и инициализируем её в
методе **onCreate()**.
Сделайте это самостоятельно, создав переменную с именем **mInfoTextView**.

Запускаем приложение и начинаем щёлкать по кнопке. При
каждом щелчке счётчик **mCount** будет увеличиваться на единицу, и
эта информация будет отображаться на экране.

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

```
public void onClick(View view) {

```

```
mInfoTextView.setText("Hello Kitty!");}

```

Мы познакомились с новым способом обработки щелчка кнопки. Какой способ вы предпочтёте - зависит от вас. Интерфейс будет выглядеть следующим образом, рис. 27.

    Теперь у вас есть чрезвычайно полезное приложение **Счётчик**

ворон**. Если преподаватель вас неожиданно спросит на занятии, почему вы**
смотрите в окно с рассеянным видом, вы можете смело достать свой телефон и
сказать, что заняты очень важным делом - считаете ворон.

**Исправляем ошибки.**

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

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

Ниже показан случай, рис. 28, когда мы сделали опечатку в названии класса **String**.

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

#### **СВЕТОФОР**

Для закрепления навыков создадим приложение, чуть посложнее предыдущего, а также познакомимся с другими приёмами программирования.

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

Первые шаги вполне очевидны. Создаём новый проект на основе "Hello, World" и перетаскиваем с панели инструментов две (пока) кнопки. Далее растягиваем их по ширине экрана. Найдите на панели инструментов над формой кнопку Toggle Fill Width, на которой изображена двусторонняя стрелка ↔. Также можете это сделать вручную - для этого в окне свойств присваиваем свойству layout:width значение match\_parent.

В окне Component Tree выделите строку button1. У вас должно появиться окно свойств Properties. Для первой кнопки присвоим свойству id значение buttonRed вместо стандартного @+id/button1, а для второй кнопки - buttonYellow.

Теперь попробуем создать третью кнопку не через визуальное проектирование, а через код. Для этого в главном окне переключитесь с вкладки Design на вкладку Text. Здесь вы увидите XML-разметку программы, в том числе и код для двух кнопок.

Наверное, вы уже догадались, как можно создать третью кнопку. Нужно просто взять за образец код любой из двух кнопок и добавить под ними новую строчку перед закрывающим тегом </RelativeLayout> (не забудьте изменить идентификатор и атрибут android:layout\_below):

```
<RelаtiveLayout xmlns:andrоid="http://schemas.android.com/apk/res/android"

```

```
xmlns:tools="http://schemas.android.com/tools"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="match_parent" >

```

```
<ТextView

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:layout_centerHorizontal="true"

```

```
android:layout_centerVertical="true"

```

```
android:text="@string/hello_world" />

```

```
<Вutton

```

```
android:id="@+id/buttonRed"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:layout_alignParentLeft="true"

```

```
android:layout_alignParentTop="true"

```

```
android:text="Button" />

```

```
<Вutton

```

```
android:id="@+id/buttonYellow"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:layout_alignParentLeft="true"

```

```
android:layout_below="@+id/buttonRed"

```

```
android:text="Button" />

```

```
<Вutton

```

```
android:id="@+id/buttonGreen"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:layout_alignParentLeft="true"

```

```
android:layout_below="@+id/buttonYellow"

```

```
android:text="Button" />

```

```
</RelativeLayout>

```

Теперь нам нужно заменить текст на кнопках на слова Красный, Жёлтый и Зелёный. В прошлый раз мы присвоили свойству Text нужную строку. Но на самом деле это неудачный подход и даже среда разработки выводит предупреждающие значки у кнопок. По правилам, строки нужно хранить в строковых ресурсах. Подобный подход даёт разработчику множество преимуществ, в частности, быструю локализацию приложения.

Процесс создания строковых ресурсов очень прост. Переключитесь обратно в режим Design и выберите кнопку buttonRed. В окне свойств выделите свойство text. Во

второй колонке отобразится кнопка с многоточием. Щёлкните на кнопке. У вас откроется диалоговое окно Resource Chooser, рис. 29.

Нажмите на кнопку New Resource для создания нового строкового ресурса и выберите New String Value. В новом окне New String Value Resource в первом поле Resource Name введите название ресурса, например, red, а во втором поле Resource Value введите текст для кнопки (напр. Красный). Остальные поля не трогаем. Аналогичным образом поступите с другими двумя кнопками (Жёлтый и Зёленый), рис. 30.

Программно можно добиться такого же результата, отредактировав файл strings.xml, который находится в папке res/values вашего проекта. Сейчас он может выглядеть так.

```
<?xml version="1.0" encoding="utf-8"?>

```

```
<resources>

```

```
<string name="app_name">TrafficLight</string>

```

```
<string name="hello_world">Hello world!</string>

```

```
<string name="action_settings">Settings</string>

```

```
<string name="red">Красный</string>

```

```
<string name="yellow">Жёлтый</string>

```

```
<string name="green">Зелёный</string>

```

```
</resources>

```

Мы совсем забыли про элемент TextView. Пусть на нём выводится текст, извещающий о текущем цвете фона приложения. Так как в ресурсах у нас уже есть слова Красный, Жёлтый и Зелёный, изначально предназначенные для кнопок, то мы не будем создавать новые строковые ресурсы, а воспользуемся готовыми наработками. По умолчанию у нас

используется красный цвет. В окне свойств выбираем свойство text для TextView и нажимаем кнопку с многоточием для вызова знакомого диалогового окна. На этот раз мы не будем щёлкать на кнопке New Resource, а сразу выберем строку red, которая, как мы помним, содержит текст Красный и щёлкнем кнопку OK (можно сделать сразу двойной щелчок на строке).

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

```
<ТextView

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:layout_centerHorizontal="true"

```

```
android:layout_centerVertical="true"

```

```
android:text="@string/red" />

```

Со строками вроде разобрались. Давайте теперь в ресурсах зададим цвета для фона программы. Ресурсы для цветов принято хранить в отдельном файле colors.xml. Сейчас у нас файла для ресурсов цвета нет, поэтому создадим его сами. Делаем щелчок правой кнопкой мыши на папке values и выбираем New | Values resource file.

В диалоговом окне Choose Destination Directory нужно выбрать папку, рис. 31. Стандартной папкой для работы является самая первая - values, она и будет выделена.

Выбрав первую папку values, нажимаем OK.

В следующем диалоговом окне вводим имя нового файла colors (суффикс .xml вводить не обязательно), рис. 32.

Созданный файл откроется для редактирования.

Добавим ресурс жёлтого цвета между тегами resources:

```
<color name="yellowColor">#FFFF00</color>

```

Слева появится жёлтый квадрат, по нему легко видеть цвет заданного ресурса.

По такому же принципу добавьте зелёный цвет.

```
<color name="greenColor">#00FF00</color>

```

Самостоятельно добавьте ресурс для красного цвета под именем redColor.

Определив в ресурсах все необходимые цвета, можно сразу присвоить красный цвет для контейнера RelativeLayout. В окне свойств находим для данного элемента свойство background и через редактор ресурсов устанавливаем ему значение @color/redColor. Либо вручную добавьте строчку android:background="@color/redColor" для тега RelativeLayout.

Общий каркас приложения завершен. У нас есть три кнопки с соответствующими текстами, текстовая надпись со словом Красный, и красный фон, используемый в контейнере RelativeLayout. Пора приступать к программной логике программы. Можно запустить приложение, чтобы убедиться, что мы не сделали ошибок в разметке.

Наша задача - обработать щелчки трёх кнопок и менять цвет фона приложения, а также текст в TextView. Мы уже познакомились с удобным способом обработки события onClick. Давайте закрепим пройденный материал и повторим тот же код для первой кнопки. Пропишем вручную событие onClick в теге Button:

```
android:onClick="onRedButtonClick"

```

Вспоминаем - в режиме Text помещаем курсор на названии метода и нажимаем комбинацию Alt+Enter, чтобы создать заготовку щелчка первой кнопки в классе MainActivity.

Объявим переменные в классе и получим к ним доступ в методе onCreate():

```
// до метода onCreate()

```

```
private RelativeLayout mRelativeLayout;

```

```
private TextView mInfoTextView;

```

```
// в методе onCreate()

```

```
mRelativeLayout = (RelativeLayout)findViewById(R.id.relativeLayout);

```

```
mInfoTextView = (TextView)findViewById(R.id.textView);

```

Также обратите внимание, что мы обращаемся к компонентам RelativeLayout и TextView по идентификаторам, которые ещё не создавали. Если вы до этого не присвоили идентификаторы для RelativeLayout и TextView, то вернитесь в файл activity\_main.xml и добавьте атрибуты android:id="@+id/relativeLayout" и android:id="@+id/textView". На будущее старайтесь сразу присваивать идентификаторы элементам, с которыми придётся работать в коде.

Пишем код для щелчка кнопки с надписью "Красный":

```
public void onRedButtonClick(View view) {

```

```
mInfoTextView.setText(R.string.red);

```

```
mRelativeLayout.setBackgroundColor

```

```
(getResources().getColor(R.color.redColor));}

```

Мы обращаемся к созданным ресурсам через специальный класс R и через точку указываем тип ресурсов, а затем имя ресурса.

Для кнопки "Зелёный" напишите код самостоятельно, добавив метод onGreenButtonClick().

Для кнопки "Жёлтый" напишем код в традиционной манере через слушателя OnClickListener. В методе onCreate() добавляем:

```
Button yellowButton = (Button)findViewById(R.id.buttonYellow);

```

```
yellowButton.setOnClickListener(new View.OnClickListener() {

```

```
@Override

```

```
public void onClick(View view) {

```

```
mInfoTextView.setText(R.string.yellow);

```

```
mRelativeLayout.setBackgroundColor

```

```
(getResources().getColor(R.color.yellowColor));} });

```

В Android 6 (API 23) метод getColor(int id) объявили устаревшим и студия теперь подчёркивает данный метод. Можно заменить на один из двух вариантов:

```
// у второго параметра используем значение null

```

```
mRelativeLayout.setBackgroundColor

```

```
(getResources().getColor(R.color.yellowColor, null));

```

```
// используем метод из библиотеки совместимости

```

```
mRelativeLayout.setBackgroundColor

```

```
(ContextCompat.getColor(this, R.color.yellowColor));

```

Запускаем приложение и щёлкаем по кнопкам - текст в надписи и фон в приложении должны меняться в соответствии с нажатой кнопкой, рис. 33.

Полный текст кода будет выглядеть следующим образом:

```
package ru.schoolbreaker.trafficlight;

```

```
import android.app.Activity;

```

```
import android.os.Bundle;

```

```
import android.view.View;

```

```
import android.widget.Button;

```

```
import android.widget.RelativeLayout;

```

```
import android.widget.TextView;

```

```
public class MainActivity extends Activity {

```

```
private RelativeLayout mRelativeLayout;

```

```
private TextView mInfoTextView;

```

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_main);

```

```
mRelativeLayout =

```

```
(RelativeLayout)findViewById(R.id.relativeLayout);

```

```
mInfoTextView = (TextView) findViewById(R.id.textView);

```

```
Button yellowButton = (Button) findViewById(R.id.buttonYellow);

```

```
yellowButton.setOnClickListener(new View.OnClickListener() {

```

```
@Override

```

```
public void onClick(View view) {

```

```
mInfoTextView.setText(R.string.yellow);

```

```
mRelativeLayout.setBackgroundColor

```

```
(getResources().getColor(R.color.yellowColor));} }); }

```

```
public void onRedButtonClick(View view) {

```

```
mInfoTextView.setText(R.string.red);

```

```
mRelativeLayout.setBackgroundColor

```

```
(getResources().getColor(R.color.redColor));}

```

```
public void onGreenButtonClick(View view) {

```

```
mInfoTextView.setText(R.string.green);

```

```
mRelativeLayout.setBackgroundColor

```

```
(getResources().getColor(R.color.greenColor));} }

```

В качестве домашнего задания упростите код для трёх кнопок, создав для них общий метод onClick().

Поговорим о значках. По умолчанию студия использует изображение зелёного робота в качестве значка для вашей программы. Откройте в студии папку res/drawable. Самый простой вариант заменить стандартное изображение на своё - создать своё изображение и заменить им имеющийся значок. Для Android Studio, начиная с версии 0.8.14, теперь можно щёлкнуть правой кнопкой на папке drawable и выбрать в меню New | Image Asset.

Откроется диалоговое окно, где вы можете указать в качестве источника файл на компьютере, вариант из клипарта или набор символов, рис. 34-35. Также вы можете задать форму значка, цвет фона и прочие параметры. Уверены, что вы разберётесь самостоятельно.

#### **ПЕРЕКЛЮЧЕНИЕ МЕЖДУ ЭКРАНАМИ ПРИЛОЖЕНИЯ**

Приложение не всегда состоит из одного экрана. Например, мы создали очень полезную программу и пользователю хочется узнать, кто же её автор. Он нажимает на кнопку "О программе" и попадает на новый экран, где находится полезная информация о версии программы, авторе, адресе сайта и т.д. Воспринимайте экран активности как веб-страницу с ссылкой на другую страницу. Если вы посмотрите на код в файле MainActivity.java из прошлых уроков, то увидите, что наш класс MainActivity тоже относится к Activity или, если говорить точнее, наследуется от него.

```
public class MainActivity extends Аctivity

```

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

Мы возьмем программу из первого урока и будем использовать для опытов кнопку (или создайте новый проект с одной кнопкой на экране). Далее создадим новую форму для отображения полезной информации. Например, покажем пользователю, что делает кот, когда идёт налево и направо (подробное описание можно найти в книге А.С.Пушкина "Руслан и Людмила").

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

Создадим новый XML-файл разметки activity\_about.xml в папке res/layout, рис. 36. Щёлкните правой кнопкой мыши на папке layout и выберите из контекстного меню New | Layout resource file. Появится диалоговое окно. В первом поле вводим имя файла activity\_about. Во втором нужно ввести корневой элемент. По умолчанию там стоит LinearLayout. Стираем текст и вводим ScrollView. Ввода нескольких символов достаточно, чтобы студия подсказала готовые варианты, можно сразу нажать Enter, не дожидаясь полного ввода слова:

Получится соответствующая заготовка, в которую вставим элемент TextView.

Информация будет извлекаться из ресурсов, а именно из строкового ресурса about\_text. Сейчас он подсвечен красным цветом, сигнализируя об отсутствии информации. Можно было нажать Alt+Enter и ввести текст в диалоговом окне. Но для нашего примера этот способ не подойдёт, так как текст будет длинный и использовать управляющие символы. Поэтому поступим по-другому. Откроем файл res/values/strings.xml и вводим следующий текст:

```
<string nаme="about_text">

```

```
У лукоморья дуб зелёный;\n

```

```
Златая цепь на дубе том:\n

```

```
И днём и ночью <b>кот учёный</b>\n

```

```
Всё ходит по цепи кругом;\n

```

```
Идёт <b>направо</b> - песнь заводит,\n

```

```
<b>Налево</b> - сказку говорит.</string>

```

Обратите внимание, что мы использовали простейшие HTML-теги форматирования текста типа <b>, <i>, <u>. Для нашего примера достаточно выделить жирным слова, которые относятся к коту и направлению движения. Для перевода текста на новую строку используйте символы \n. Добавим ещё один строковый ресурс для заголовка нового экрана:

```
<string name="about_title">О программе</string>

```

С разметкой разобрались. Далее необходимо создать класс для окна AboutActivity.java. Выбираем в меню File | New | Java Class и заполняем нужные поля, рис. 37.

Получим заготовку.

Сейчас класс практически пустой. Добавим код вручную. Класс должен наследоваться от абстрактного класса Activity или его родственников типа FragmentActivity, AppCompatActivityи т.д. Дописываем extends Activity. У класса активности должен быть метод onCreate(). Ставим курсор мыши внутри класса и выбираем в меню Code | Override Methods (Ctrl+O). В диалоговом окне ищем нужный класс, можно набирать на клавиатуре первые символы для быстрого поиска. В созданном методе нужно вызвать метод setContentView(), который подгрузит на экран подготовленную разметку. У нас получится такой вариант.

```
package ru.schoolbreaker.helloworld;

```

```
import android.app.Activity;

```

```
import android.os.Bundle;

```

```
public class AboutActivity extends Activity {

```

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_about);} }

```

Теперь начинается самое главное. Наша задача - перейти на новый экран по щелчку кнопки на первом экране. Переходим обратно к классу MainActivity, где пишем обработчик щелчка кнопки:

```
public void onClick(View view) {

```

```
Intent intent = new Intent(MainActivity.this, AboutActivity.class);

```

```
stаrtActivity(intent);}

```

Для запуска нового экрана необходимо создать экземпляр класса Intent и указать в первом параметре текущий класс, а во втором - класс для перехода, у нас это AboutActivity. После этого вызывается метод startActivity(), который и запускает новый экран.

Если вы сейчас попытаетесь проверить работу приложения в эмуляторе, то получите сообщение об ошибке. Что мы сделали неправильно? Мы пропустили один важный шаг. Необходимо зарегистрировать новый Activity в манифесте AndroidManifest.xml. Найдите этот файл в своем проекте и дважды щёлкните на нём. Откроется окно редактирования файла. Добавьте новый тег <activity> после закрывающего тега </activity> для первой активности. Печатайте самостоятельно и активно используйте подсказки. Получится следующее:

```
<аctivity android:nаme=".AbоutActivity"

```

```
andrоid:label="@string/about_title">

```

```
</аctivity>

```

Вот и пригодился строковый ресурс about\_title. Запускаем приложение, щёлкаем на кнопке и получаем окно О программе. Таким образом, мы научились создавать новое окно и вызывать его по щелчку кнопки. А в нашем распоряжении появилась мегаудобная программа - теперь всегда под рукой будет подсказка, что делает кот, когда идёт налево, рис. 38.

Ещё раз обращаем внимание, что второй создаваемый класс активности должен наследоваться от класса Activity или ему похожих (ListActivity и др.), иметь XML-файл разметки (если требуется) и быть прописан в манифесте.

После вызова метода startActivity() запустится новая активность (в данном случае AboutActivity), она станет видимой и переместится на вершину стека, содержащего работающие компоненты. При вызове метода finish() из новой активности (или при нажатии аппаратной клавиши возврата) она будет закрыта и удалена из стека. Разработчик также может перемещаться к предыдущей (или к любой другой) активности, используя всё тот же метод startActivity().

Теперь мы с вами рассмотрим способ создания активности с помощью мастера.

В этом случае выберите в меню File | New | Activity | Blank Activity . Дальше появится знакомое вам окно создания новой активности, рис. 39. Заполняем необходимые поля. Предпоследнее поле Hierarchical Parent: можно оставить пустым, либо указать родителя, например, MainActivity. В этом случае, при нажатии на кнопку Back вы сразу вернётесь на главный экран приложения. Иногда бывает полезным.

Нажимаем на кнопку Finish и активность будет готова. Чтобы убедиться в этом, откройте файл манифеста и проверьте наличие новой записи. Самостоятельно добавьте новую кнопку на экране главной активности и напишите код для перехода на созданную активность.

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

Область extraData - это список пар ключ/значение, который передаётся вместе с намерением. В качестве ключей используются строки, а для значений можно использовать любые примитивные типы данных, массивы примитивов, объекты класса Bundle и др.

Для передачи данных в другую активность используется метод putExtra():

```
intent.putExtra("Ключ", "Значение");

```

Принимающая активность должна вызвать какой-нибудь подходящий метод: getIntExtra(), getStringExtra() и т.д.:

```
int count = getIntent().getIntExtra("name", 0);

```

Переделаем наш пример. У нас уже есть три активности. У первой активности разместим два текстовых поля и кнопку. Внешний вид может быть как на рис. 40:

У второй активности SecondActivity установим элемент TextView, в котором будем выводить текст, полученный от первой активности. Напишем следующий код для метода onCreate() у второй активности.

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_second);

```

```
String user = "ЖЫвотное";

```

```
String gift = "дырку от бублика";

```

```
TextView infoTextView = (TextView)findViewById(R.id.textViewInfo);

```

```
infoTextView.setText(user + " , вам передали " + gift);}

```

Если сейчас запустить программу и просто вызвать второе окно, как это было описано в первой части статьи, то мы увидим надпись по умолчанию ЖЫвотное, вам передали дырку от бублика. Согласитесь, довольно обидно получать такие сообщения.

Исправляем ситуацию. Добавляем код у первой активности:

```
public void onClick(View view) {

```

```
EditText userEditText = (EditText) findViewById(R.id.editTextUser);

```

```
EditText giftEditText = (EditText) findViewById(R.id.editTextGift);

```

```
Intent intent =

```

```
new Intent(MainActivity.this, SecondActivity.class);

```

```
// в ключ username вставляем текст из первого текстового поля

```

```
intent.putExtra("username", userEditText.getText().toString());

```

```
// в ключ gift вставляем текст из второго текстового поля

```

```
intent.putExtra("gift", giftEditText.getText().toString());

```

```
startActivity(intent);}

```

Мы поместили в специальный контейнер объекта Intent два ключа со значениями, которые берутся из текстовых полей. Когда пользователь введёт данные в текстовые поля, они попадут в этот контейнер и будут переданы второй активности, рис. 41.

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

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

```

```
String user = "ЖЫвотное";

```

```
String gift = "дырку от бублика";

```

```
user = getIntent().getExtras().getString("username");

```

```
gift = getIntent().getExtras().getString("gift");

```

```
TextView infoTextView = (TextView)findViewById(R.id.textViewInfo);

```

```
infoTextView.setText(user + " , вам передали " + gift);

```

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

В нашем случае мы знаем, что ждём строковое значение, поэтому код можно переписать так:

```
Intent intent = getIntent();

```

```
user = intent.getStringExtra("username");

```

Или так:

```
user = getIntent().getStringExtra("username");

```

У программы есть недостаток - не понятно, от кого мы получаем приветы. Любая хорошо воспитанная мартышка не возьмет подарка от анонимного источника. Поэтому в качестве домашнего задания добавьте ещё одно текстовое поле для ввода имени пользователя, который отправляет сообщение.

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

```
public final static String USER = "ru.schoolbreaker.myapp.USER";

```

Не всегда бывает достаточно просто передать данные другой активности. Иногда требуется получить информацию обратно от другой активности при её закрытии. Если раньше мы использовали метод startActivity(Intent intent), то существует родственный ему метод startActivityForResult(Intent intent, int RequestCode). Разница между методами заключается в дополнительном параметре RequestCode. По сути это просто целое число, которое вы можете сами придумать. Оно нужно для того, чтобы различать от кого пришёл

результат. Допустим у вас есть пять дополнительных экранов и вы присваиваете им значения от 1 до 5, и по этому коду вы сможете определить, чей результат вам нужно обрабатывать. Вы можете использовать значение -1, тогда это будет равносильно вызову метода startActivity(), т.е. никакого результата не получим.

Если вы используете метод startActivityForResult(), то вам необходимо переопределить в коде метод для приёма результата onActivityResult() и обработать полученный результат. Запутались? Давайте разберём пример.

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

Один из посетителей предоставил серию фотографий со своего айфона. Также имеются показания другого свидетеля: А Васька слушает, да ест.

Создаём новый проект Sherlock с двумя активностями. На первом экране будет кнопка для переключения на второй экран и текстовая метка, в которой будет отображено имя воришки.

```
<?хml version="1.0" encoding="UTF-8"?>

```

```
<LinеarLayout xmlns:android="http://schemas.android.com/apk/res/android"

```

```
andrоid:id="@+id/LinearLayout1"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="match_parent"

```

```
android:orientation="vertical"

```

```
android:padding="5dp" >

```

```
<ImаgeViеw

```

```
android:id="@+id/icon"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:adjustViewBounds="true"

```

```
android:background="#fa2255"

```

```
android:scaleType="centerCrop"

```

```
android:src="@drawable/sherlock" />

```

```
<ТextView

```

```
android:id="@+id/textViewLabel"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:text="@string/who"

```

```
android:textAppearance="?android:attr/textAppearanceMedium" />

```

```
<ТextView

```

```
android:id="@+id/textViewInfo"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:textAppearance="?android:attr/textAppearanceLarge" />

```

```
<Вutton

```

```
android:id="@+id/buttonChoose"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:onClick="onClick"

```

```
android:text="Сделать выбор" />

```

```
</LinеarLayout>

```

На втором экране будет группа переключателей:

```
<?хml version="1.0" encoding="utf-8"?>

```

```
<LinеarLаyout xmlns:android="http://schemas.android.com/apk/res/android"

```

```
android:id="@+id/LinearLayout1"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="match_parent"

```

```
android:orientation="vertical"

```

```
android:padding="10dip" >

```

```
<ТextView

```

```
android:id="@+id/textView1"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:text="Выберите правильный ответ"

```

```
android:textAppearance="?android:attr/textAppearanceMedium" />

```

```
<RаdioGrоup

```

```
android:id="@+id/radioGroup1"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content" >

```

```
<RаdioВutton

```

```
android:id="@+id/radioCrow"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:checked="false"

```

```
android:onClick="onRadioClick"

```

```
android:text="Ворона" />

```

```
<RаdioВutton

```

```
android:id="@+id/radioDog"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:onClick="onRadioClick"

```

```
android:text="Пёсик" />

```

```
<RаdioВutton

```

```
android:id="@+id/radioCat"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:onClick="onRadioClick"

```

```
android:text="Кот Васька" />

```

```
</RаdioGrоup>

```

```
</LinearLayout>

```

Так как мы будем ожидать ответ из второго экрана, то нам необходимо задействовать метод startActivityForResult() на первом экране, в котором мы передадим переменную CHOOSE\_THIEF в качестве параметра RequestCode.

```
static final private int CHOOSE_THIEF = 0;

```

```
public void onClick(View v) {

```

```
Intent questionIntent = new Intent(MainActivity.this,

```

```
ChooseActivity.class);

```

```
startActivityForResult(questionIntent, CHOOSE_THIEF);}

```

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

Переходим на второй экран и будем писать код для второй активности.

```
public final static String THIEF = "ru.schoolbreaker.sherlock.THIEF";

```

```
public void onRadioClick(View v) {

```

```
Intent answerIntent = new Intent();

```

```
switch (v.getId()) {

```

```
case R.id.radioDog:

```

```
answerIntent.putExtra(THIEF, "Пёсик");

```

```
break;

```

```
case R.id.radioCrow:

```

```
answerIntent.putExtra(THIEF, "Ворона");

```

```
break;

```

```
case R.id.radioCat:

```

```
answerIntent.putExtra(THIEF, "Лошадь Пржевальского");

```

```
break;

```

```
default:

```

```
break;}

```

```
setResult(RESULT_OK, answerIntent);

```

```
finish();}

```

Здесь всё просто, когда сыщик выбирает имя преступника, то через метод putExtra() мы передаём имя ключа и его значение.

Для удобства, после выбора мы сразу закрываем второе окно и перед закрытием передаём значение RESULT\_OK, чтобы было понятно, что выбор сделан. Если пользователь закроет экран через кнопку Back, то будет передано значение RESULT\_CANCELED.

Метод setResult() принимает два параметра: результирующий код и сам результат, представленный в виде намерения. Результирующий код говорит о том, с каким результатом завершилась работа активности, как правило, это либо Activity.RESULT\_OK, либо Activity.RESULT\_CANCELED. В некоторых случаях нужно использовать собственный код возврата для обработки специфических для вашего приложения вариантов. Метод setResult() поддерживает любое целочисленное значение.

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

Если активность была закрыта пользователем при нажатии аппаратной кнопки возврата или если метод finish() был вызван раньше, чем метод setResult(), результирующий код установится в RESULT\_CANCELED, а возвращенное намерение покажет значение null.

Возвращаемся на первый экран. Первый экран ожидает ответа от второго экрана, поэтому нужно добавить в код метод onActivityResult().

```
@Override

```

```
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

```

```
super.onActivityResult(requestCode, resultCode, data);

```

```
TextView infoTextView = (TextView) findViewById(R.id.textViewInfo);

```

```
if (requestCode == CHOOSE_THIEF) {

```

```
if (resultCode == RESULT_OK) {

```

```
String thiefname = data.getStringExtra(ChooseActivity.THIEF);

```

```
infoTextView.setText(thiefname);

```

```
}else {

```

```
infoTextView.setText(""); // стираем текст} } }

```

Метод ожидает входящие данные с кодом CHOOSE\_THIEF, и если такие данные поступят, то извлекает значение из ключа ChooseActivity.THIEF с помощью метода getStringExtra. Полученное значение мы выводим в TextView (переменная infoTextView). Если мы вернулись на экран через кнопку Back, то просто стираем текст.

При закрытии дочерней активности внутри родительского компонента срабатывает обработчик onActivityResult(). Обработчик onActivityResult() принимает несколько параметров.

* Код запроса. Код, который использовался для запуска активности, возвращающей результат
* Результирующий код. Код результата, устанавливаемый дочерней активностью и указывающий, как завершилась её работа. Это может быть любое целочисленное значение, но, как правило, либо Activity.RESULT\_OK, либо Activity.RESULT\_CANCELED
* Данные. Намерение, используемое для упаковки возвращаемых данных. В зависимости от назначения дочерней активности оно может включать путь URI, представляющий выбранную часть содержимого. В качестве альтернативы (или дополнения) дочерняя активность может возвращать информацию в виде простых значений, упакованных в параметр намерения extras

Если работа дочерней активности завершилась непредвиденно или если перед её закрытием не был указан код результата, этот параметр станет равен Activity.RESULT\_CANCELED.

Запускаем проект, нажимаем на кнопку и переходим на второй экран. Там выбираем один из вариантов. Если выбрать ворону, то экран закроется и имя преступника отобразится на первом экране. Если выбрать пёсика, то отобразится его имя.

Между прочим, если выбрать котика, то его имя не отобразится! Проверьте и убедитесь сами. Вы спросите почему? Элементарно, Ватсон! Преступник не учёл одной важной детали. В ресторане велось наблюдение с видеокамер, и запись показала, кто на самом деле украл колбаску и подставил кота.

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

Показан распространённый способ перехода на другую активность, когда в

методе startActivity() указывается текущий класс и класс для перехода. Кстати, класс активности не обязательно должен быть частью вашего приложения. Если вы знаете имя класса из другого приложения, то можете перейти и на него. Но можно перейти в другую активность другим способом.

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

```
<асtivity

```

```
andrоid:name=".SecondActivity"

```

```
android:label="@string/title_activity_second" >

```

```
<intеnt-filtеr >

```

```
<action android:name="ru.schoolbreaker.testapplication.SecondActivity" />

```

```
<саtegory andrоid:name="android.intent.category.DEFAULT" />

```

```
</intеnt-filter>

```

```
</асtivity>

```

И запускаем вторую активность через щелчок кнопки таким способом.

```
public void onClick(View view) {

```

```
startActivity(new Intent("ru.schoolbreaker.testapplication.SecondActivity"));}

```

Заменим длинную строку на константу.

```
public static final String ACTION_SECOND_ACTIVITY =

```

```
"ru.schoolbreaker.testapplication.SecondActivity";

```

```
public void onClick(View view) {

```

```
startActivity(new Intent(ACTION_SECOND_ACTIVITY));}

```

Итак, что мы сделали. Для второй активности мы прописали фильтр и указали имя для action в атрибуте android:name. Для удобства поместили в него полное имя активности с названием пакета. Конструктор класса Intent имеет несколько перегруженных версий. В одной из версий можно указать строку для действия. Мы

указали своё созданное действие, которое прописано у второй активности. Система во время работы просматривает манифесты всех установленных приложений. При поиске соответствия система находит наш фильтр и запускает нужную активность.

Имя категории фильтра android.intent.category.DEFAULT говорит системе, что следует выполнить действие по умолчанию, а именно, запустить активность. Существуют и другие имена, которые пока нас не интересуют.

Что произойдёт, если создать ещё одну активность и указать такой же фильтр, как у второй активности? Давайте проверим. Создайте у себя третью активность и скопируйте блок с фильтром от второй активности в него.

```
<асtivity

```

```
android:name=".ThirdActivity"

```

```
android:label="@string/title_activity_third" >

```

```
<intеnt-filtеr>

```

```
<action android:name="ru.schoolbreaker.testapplication.SecondActivity" />

```

```
<category android:name="android.intent.category.DEFAULT" />

```

```
</intеnt-filter>

```

```
</асtivity>

```

Щёлкаем по кнопке в первой активности. Система попросит выбрать нужный вариант, рис. 43.

Если вы выберите пункт ALWAYS, то в следующий раз выбирать не придётся. Чтобы сбросить выбор, зайдите в свойства приложения в Настройках и найдите кнопку Clear defaults.

#### **ОРИЕНТАЦИЯ**

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

Всего существует два режима - портретный и альбомный. На большинстве телефонов используется по умолчанию портретный режим (как на паспорте). Альбомный режим знаком нам по обычным мониторам.

Рассмотрим следующий случай. Предположим, у нас в приложении имеется одно текстовое поле и шесть кнопок, рис. 44. Вроде всё нормально.

Но стоит нам повернуть устройство на 90 градусов (для эмулятора нужно нажать комбинацию клавиш Ctrl+F11), как сразу обнаруживаются проблемы. Пятая кнопка видна частично, а шестая оказалась за пределами видимости, рис. 45.

Чтобы избежать такой проблемы, необходимо как-то по-другому скомпоновать кнопки. Например, расположить их не подряд друг за другом, а разбить на пары. Воспользуемся контейнером TableLayout. С его помощью мы можем разбить кнопки на две колонки и поместить их в три ряда.

Для этой операции нам понадобится сделать несколько важных шагов. Сначала нужно создать новую подпапку в папке res. Выделяем папку res, вызываем из него контекстное меню и последовательно выбираем команды New | Android resource directory. В диалоговом окне из выпадающего списка Resource type: выбираем layout. В списке Available qualifiers: находим элемент Orientation и переносим его в правую

часть Chosen qualifiers: с помощью кнопки с двумя стрелками. По умолчанию у вас появится имя папки layout-port в первой строке Directory Name:. Нам нужен альбомный вариант, поэтому в выпадающем списке Screen orientation выбираем Landscape. Теперь название папки будет layout-land, рис. 46.

Теперь нам нужно создать в созданной папке новый XML-файл activity\_main.xml. Вызываем контекстное меню у папки layout-land и выбираем команды New | Layout Resource File. В диалоговом окне присваиваем имя activity\_main.xml, которое должно совпадать с именем существующего файла. Во втором поле вводим LinearLayout, по мере ввода появится подсказка, облегчающая выбор, рис. 47.

Откроем созданный файл и модифицируем его следующим образом.

```
<?хml version="1.0" encoding="utf-8"?>

```

```
<LinеarLаyout

```

```
хmlns:android="http://schemas.android.com/apk/res/android"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="match_parent"

```

```
android:orientation="vertical">

```

```
<LinеarLаyout

```

```
android:orientation="vertical"

```

```
android:layout_height="wrap_content"

```

```
android:layout_width="match_parent"

```

```
android:layout_gravity="center"

```

```
android:paddingLeft="20dp"

```

```
android:paddingRight="20dp">

```

```
<ЕditТext

```

```
android:layout_height="wrap_content"

```

```
android:id="@+id/editText1"

```

```
android:layout_width="match_parent"/>

```

```
<ТableLаyout

```

```
android:layout_height="wrap_content"

```

```
android:layout_width="wrap_content"

```

```
android:layout_gravity="center"

```

```
android:stretchColumns="*">

```

```
<ТableRоw>

```

```
<Вutton

```

```
android:id="@+id/button1"

```

```
android:layout_height="wrap_content"

```

```
android:layout_width="match_parent"

```

```
android:text="Первая кнопка"/>

```

```
<Вutton

```

```
android:id="@+id/button2"

```

```
android:layout_height="wrap_content"

```

```
android:layout_width="match_parent"

```

```
android:text="Вторая кнопка"/>

```

```
</TаbleRow>

```

```
<TаbleRow>

```

```
<Вutton

```

```
android:id="@+id/button3"

```

```
android:layout_height="wrap_content"

```

```
android:layout_width="match_parent"

```

```
android:text="Третья кнопка"/>

```

```
<Вutton

```

```
android:id="@+id/button4"

```

```
android:layout_height="wrap_content"

```

```
android:layout_width="match_parent"

```

```
android:text="Четвёртая кнопка"/>

```

```
</TаbleRow>

```

```
<TаbleRow>

```

```
<Вutton

```

```
android:id="@+id/button5"

```

```
android:layout_height="wrap_content"

```

```
android:layout_width="match_parent"

```

```
android:text="Пятая кнопка"/>

```

```
<Вutton

```

```
android:id="@+id/button6"

```

```
android:layout_height="wrap_content"

```

```
android:layout_width="match_parent"

```

```
android:text="Шестая кнопка"/>

```

```
</TаbleRow>

```

```
</TаbleLayout>

```

```
</LinеarLayout>

```

```
</LinеarLayout>

```

Запускаем приложение и проверяем. Отлично, теперь видны все кнопки, рис. 48.

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

Чтобы из кода узнать текущую ориентацию, можно создать следующую функцию:

```
private String getScreenOrientation(){

```

```
if(getResources().getConfiguration().orientation

```

```
== Configuration.ORIENTATION_PORTRAIT)

```

```
return "Портретная ориентация";

```

```
else if (getResources().getConfiguration().orientation

```

```
== Configuration.ORIENTATION_LANDSCAPE)

```

```
return "Альбомная ориентация";

```

```
else

```

```
return "";}

```

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

Можно также вычислить ширину и высоту экрана, если высота больше ширины, то устройство в портретной ориентации, иначе - в альбомной:

```
private String mOrientation = "";

```

```
private boolean isLandscapeMode(Activity activity)

```

```
{int width =

```

```
activity.getWindowManager().getDefaultDisplay().getWidth();

```

```
int height =

```

```
activity.getWindowManager().getDefaultDisplay().getHeight();

```

```
boolean isLandscape = width > height;

```

```
if(isLandscape)

```

```
mOrientation = "Альбомная";

```

```
else

```

```
mOrientation = "Портретная";

```

```
return isLandscape;}

```

Сейчас этот код считается устаревшим и для вычисления размера экрана используются другие методы.

Хорошо, мы можем определить текущую ориентацию, но в какую сторону повернули устройство? Ведь его можно повернуть влево, вправо или вообще вверх тормашками. Напишем другую функцию:

```
private String getRotateOrientation() {

```

```
int rotate =

```

```
getWindowManager().getDefaultDisplay().getRotation();

```

```
switch (rotate) {

```

```
case Surface.ROTATION_0:

```

```
return "Не поворачивали";

```

```
case Surface.ROTATION_90:

```

```
return "Повернули на 90 градусов по часовой стрелке";

```

```
case Surface.ROTATION_180:

```

```
return "Повернули на 180 градусов";

```

```
case Surface.ROTATION_270:

```

```
return "Повернули на 90 градусов против часовой стрелки";

```

```
default:

```

```
return "Не понятно";} }

```

Если вы хотите запустить приложение в стиле "вид сбоку", то можете сделать это программно. Разместите код в методе onCreate():

```
import android.content.pm.ActivityInfo; //Добавить вверху документа

```

```
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

```

```
//Добавить в методе onCreate

```

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

```
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

```

```
//для альбомного режима

```

```
// или

```

```
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

```

```
//для портретного режима

```

Но указанный способ нежелателен. Лучше установить нужную ориентацию через манифест, прописав в элементе <activity> параметр android:screenOrientation:

```
android:screenOrientation="portrait"

```

```
android:screenOrientation="landscape"

```

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

```
android:screenOrientation="sensor"

```

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

ориентацию. Заодно будем менять текст на кнопке, чтобы операция соответствовала надписи. Прежде чем продолжить, в активности оставьте только одну кнопку Button и дайте ей идентификатор button1.

```
public class MainActivity extends Activity {

```

```
Button mButton;

```

```
static final String ORIENTATION_PORTRAIT = "Портретный режим";

```

```
static final String ORIENTATION_LANDSCAPE = "Альбомный режим";

```

```
// определяем изменение ориентации экрана

```

```
boolean mState = false;

```

```
/** Called when the activity is first created. */

```

```
@Override

```

```
public void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_main);

```

```
mButton = (Button) findViewById(R.id.button1);

```

```
// set default text display

```

```
mButton.setText(ORIENTATION_LANDSCAPE);}

```

```
public void onClick(View v) {

```

```
// state FALSE: переключаемся на LANDSCAPE

```

```
if (!mState) {

```

```
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

```

```
mButton.setText(ORIENTATION_PORTRAIT);}

```

```
// state TRUE: переключаемся на PORTRAIT

```

```
else {

```

```
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

```

```
mButton.setText(ORIENTATION_LANDSCAPE);}

```

```
// обновляем state на противоположное значение

```

```
mState = !mState;} }

```

Теперь посмотрите, что у нас получилось. Запустите проект и нажмите на кнопку. Ориентация экрана поменялась, однако текст на кнопке остался прежним, хотя по нашей задумке он должен измениться, рис. 49.

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

По умолчанию, при смене ориентации Android уничтожает и пересоздаёт активность из кода, что подразумевает повторный вызов метода onCreate(). Поэтому при повороте активность устанавливала текст, определенный в onCreate(). В большинстве случаев это не мешает программе. Но если приложение воспроизводит видео, то при смене ориентации вызов onCreate() может привести к повторному началу воспроизведения (если так написан пример).

Чтобы активность не пересоздавалась, добавьте в манифест строчку для нужной активности:

```
android:configChanges="keyboardHidden|orientation|screenSize"

```

В этом случае система вызывает метод onConfigurationChanged(Configuration) и полагается на вас (т.е. нужно добавить свою реализацию данного метода):

```
@Override

```

```
public void onConfigurationChanged(Configuration newConfig) {

```

```
super.onConfigurationChanged(newConfig);

```

```
// Проверяем ориентацию экрана

```

```
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

```

```
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();

```

```
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

```

```
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();} }

```

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

```
Button landscapeButton = (Button) findViewById(R.id.landscapeButton);

```

```
if (landscapeButton != null) {

```

```
// Можно работать}

```

На практике такое встречается редко, но помните на всякий случай.

#### **МЕНЮ**

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

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

Создайте новый проект и запустите его. Нажмите на значок меню в правой части заголовка, чтобы увидеть пункт меню Settings, рис. 50.

Попробуем разобраться, как это устроено. Само слово Settings хранится в файле res/values/strings.xml под именем action\_settings. Давайте создадим ещё три новых элемента по такому же принципу:

```
<string name="action_cat_male">Кот</string>

```

```
<string name="action_cat_female">Кошка</string>

```

```
<string name="action_kitten">Котёнок</string>

```

Теперь найдите папку res/menu. В ней есть файл menu\_main.xml - имя указывает, что меню относится к основной активности MainActivity. Если вы будете создавать приложение с несколькими экранами, то у каждой активности будет отдельное меню со своими настройками. Пока откроем файл menu\_main.xml для изучения:

Для Android 4.0

```
<mеnu xmlns:android="http://schemas.android.com/apk/res/android" >

```

```
<itеm

```

```
android:id="@+id/action_settings"

```

```
android:orderInCategory="100"

```

```
android:showAsAction="never"

```

```
android:title="@string/action_settings"/>

```

```
</mеnu>

```

В Android 5.0 имеются небольшие отличия. Атрибут showAsAction теперь относится к пространству имён app.

```
<mеnu xmlns:android="http://schemas.android.com/apk/res/android"

```

```
хmlns:app="http://schemas.android.com/apk/res-auto"

```

```
хmlns:tools="http://schemas.android.com/tools"

```

```
tоols:context=".MainActivity">

```

```
<itеm

```

```
android:id="@+id/action_settings"

```

```
android:title="@string/action_settings"

```

```
android:orderInCategory="100"

```

```
app:showAsAction="never" />

```

```
</mеnu>

```

Элемент item отвечает за отдельный пункт меню. Добавим ещё три пункта по такому же принципу, меняя только идентификатор и текст для меню:

```
<mеnu xmlns:android="http://schemas.android.com/apk/res/android"

```

```
хmlns:app="http://schemas.android.com/apk/res-auto"

```

```
хmlns:tools="http://schemas.android.com/tools"

```

```
tоols:context=".MainActivity">

```

```
<itеm

```

```
android:id="@+id/action_settings"

```

```
android:title="@string/action_settings"

```

```
android:orderInCategory="100"

```

```
app:showAsAction="never" />

```

```
<itеm

```

```
android:id="@+id/action_cat1"

```

```
android:orderInCategory="100"

```

```
app:showAsAction="never"

```

```
android:title="@string/action_cat_male" />

```

```
<itеm

```

```
android:id="@+id/action_cat2"

```

```
android:orderInCategory="100"

```

```
app:showAsAction="never"

```

```
android:title="@string/action_cat_female" />

```

```
<itеm

```

```
android:id="@+id/action_cat3"

```

```
android:orderInCategory="100"

```

```
app:showAsAction="never"

```

```
android:title="@string/action_kitten" />

```

```
</mеnu>

```

Запустите проект и попробуйте снова вызвать меню. Вы увидите три новых пункта, рис. 51.

Атрибут app:showAsAction="never" указывает, что пункты меню никогда не должны выводиться в заголовке приложения, а всегда находиться за тремя точками. Если заменить на значение ifRoom, то пункт сразу выведется в заголовке. Если таких пунктов будет много, то они будут только мешаться. Как правило, в таком варианте выводят очень короткое слово или значок для частых операций, чтобы избежать лишнего щелчка на три точки.

Пока пункты меню не выполняют полезной работы. Любое нажатие на пункт просто закрывает меню без видимых последствий. Мы ещё не написали код для обработки нажатий. Откроем файл MainActivity.java и изучим его.

В нём вы уже видели код для метода onCreateOptionsMenu(). Именно данный метод отвечает за появление меню у активности. Закомментируйте вызов метода и убедитесь, что меню не будет выводиться.

```
@Override

```

```
public boolean onCreateOptionsMenu(Menu menu) {

```

```
getMenuInflater().inflate(R.menu.menu_main, menu);

```

```
return true;}

```

В методе inflate() вы указываете на ресурс меню (R.menu.menu\_main) и объект класса Menu. Вся подготовительная работа сделана за вас и вам не нужно ничего помнить.

Мы научились создавать меню. Но пока оно бесполезно, так как пункты меню никак не реагируют на наши нажатия. Для обработки нажатий пунктов меню служит метод onOptionsItemSelected(). Данный метод тоже уже присутствует в проекте и вам не надо ничего помнить. А вот раньше программистам приходилось добавлять метод вручную.

```
@Override

```

```
public boolean onOptionsItemSelected(MenuItem item) {

```

```
// Handle action bar item clicks here. The action bar will

```

```
// automatically handle clicks on the Home/Up button, so long

```

```
// as you specify a parent activity in AndroidManifest.xml.

```

```
int id = item.getItemId();

```

```
//noinspection SimplifiableIfStatement

```

```
if (id == R.id.action_settings) {

```

```
return true;}

```

```
return super.onOptionsItemSelected(item);}

```

В методе надо указать идентификатор пункта меню и код, который должен выполняться для него. В заготовке используется оператор if. Мы заменим его на switch для удобства, но вы можете использовать и конструкцию if else. Для вывода информации воспользуемся текстовой меткой. Добавьте на экран активности компонент TextView. Можете использовать имеющийся TextView с надписью "Hello World!", только присвойте ему идентификатор.

```
@Override

```

```
public boolean onOptionsItemSelected(MenuItem item) {

```

```
// Handle action bar item clicks here. The action bar will

```

```
// automatically handle clicks on the Home/Up button, so long

```

```
// as you specify a parent activity in AndroidManifest.xml.

```

```
int id = item.getItemId();

```

```
TextView infoTextView = (TextView) findViewById(R.id.textViewInfo);

```

```
// Операции для выбранного пункта меню

```

```
switch (id) {

```

```
case R.id.action_cat1:

```

```
infoTextView.setText("Вы выбрали кота!");

```

```
return true;

```

```
case R.id.action_cat2:

```

```
infoTextView.setText("Вы выбрали кошку!");

```

```
return true;

```

```
case R.id.action_cat3:

```

```
infoTextView.setText("Вы выбрали котёнка!");

```

```
return true;

```

```
default:

```

```
return super.onOptionsItemSelected(item);} }

```

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

Существует альтернативный способ через XML, похожий на обработку щелчков кнопки (начиная с Android 3.0). Вы можете добавить атрибут android:onClick в ресурсах меню, и вам не нужно использовать вызов метода onOptionsItemSelected(). При помощи android:onClick вы можете указать нужный метод при выборе пункта меню. Добавьте данный атрибут к пункту Settings.

```
<itеm

```

```
android:id="@+id/action_settings"

```

```
android:title="@string/action_settings"

```

```
android:orderInCategory="100"

```

```
android:onClick="onSettingsMenuClick"

```

```
app:showAsAction="never" />

```

Теперь в коде активности напишем следующее:

```
// у атрибута пункта меню Settings установлено значение android:onClick="onSettingsMenuClick"

```

```
public void onSettingsMenuClick(MenuItem item) {

```

```
TextView infoTextView = (TextView) findViewById(R.id.textViewInfo);

```

```
infoTextView.setText("Вы выбрали пункт Settings, лучше бы выбрали кота");}

```

Внешний вид пунктов меню можно изменить на вид с переключателями, рис. 53. Для этого нужно добавить элемент group с атрибутом android:checkableBehavior="single":

```
<mеnu xmlns:android="http://schemas.android.com/apk/res/android"

```

```
хmlns:app="http://schemas.android.com/apk/res-auto"

```

```
хmlns:tools="http://schemas.android.com/tools"

```

```
tоols:context=".MainActivity">

```

```
<itеm

```

```
android:id="@+id/action_settings"

```

```
android:orderInCategory="100"

```

```
android:title="@string/action_settings"

```

```
app:showAsAction="never" />

```

```
<grоup android:checkableBehavior="single">

```

```
<itеm

```

```
android:id="@+id/action_cat1"

```

```
android:orderInCategory="100"

```

```
android:title="@string/action_cat_male"

```

```
app:showAsAction="never" />

```

```
<itеm

```

```
android:id="@+id/action_cat2"

```

```
android:orderInCategory="100"

```

```
android:title="@string/action_cat_female"

```

```
app:showAsAction="never" />

```

```
<itеm

```

```
android:id="@+id/action_cat3"

```

```
android:orderInCategory="100"

```

```
android:title="@string/action_kitten"

```

```
app:showAsAction="never" />

```

```
</grоup>

```

```
</mеnu>

```

#### **КАЛЬКУЛЯТОР ГЕОМЕТРИЧЕСКИХ ФИГУР**

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

#### **POPUPMENU - ВСПЛЫВАЮЩЕЕ МЕНЮ**

Начиная с Android 3.0, в системе появилась возможность создавать всплывающее меню, привязанное к элементу View. Меню реализовано в виде модального окна, которое отображается снизу от родителя меню или в другом месте, если места снизу недостаточно. PopupMenu не нужно путать с контекстным меню. У них разные задачи, хотя поведение схоже. В новых версиях Android использование всплывающих меню предпочтительнее контекстных, которые можно считать устаревшим интерфейсом.

В Android 4.0 добавили новую функциональность, чтобы работать было проще. В частности, всплывающее меню можно получить из XML-файла, используя метод inflate(int), которому следует передать идентификатор ресурса меню. А до этого приходилось использовать отдельный класс MenuInflator с избыточным кодом.

Также появился слушатель PopupMenu.OnDismissListener для работы с закрытием меню. Он срабатывает либо, когда пользователь щёлкает на пункте меню и меню закрывается, либо пользователь щёлкает в другом месте экрана, и меню также закрывается.

Создать всплывающее меню очень просто. Мы повторяем шаги по созданию обычного меню. Сначала в ресурсах меню создадим нужный файл:

* rеs/mеnu/pоpupmenu.xml

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

Далее добавим на экран активности текстовую метку, кнопку и ImageView. При щелчке на каждом из этих компонентов мы будем выводить одинаковое всплывающее меню:

```
<?хml version="1.0" encoding="utf-8"?>

```

```
<LinеarLаyout хmlns:аndroid="httр://sсhemas.аndroid.cоm/аpk/rеs/аndroid"

```

```
andrоid:layout_width="match_parent"

```

```
android:layout_height="match_parent"

```

```
android:gravity="center_horizontal"

```

```
android:orientation="vertical">

```

```
<вutton

```

```
android:id="@+id/button"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:text="Показать меню" />

```

```
<ТextViеw

```

```
android:id="@+id/textView"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:text="A TextView" />

```

```
<ImаgeViеw

```

```
android:id="@+id/imageView"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="match_parent"

```

```
android:src="@drawable/ic_launcher_cat" />

```

```
</LinеarLayout>

```

Осталось написать код. Обратите внимание на закомментированный код, который работал в Android 3.0. В Android 4.0 этот код можно не использовать.

```
package ru.schoolbreaker.testapplication;

```

```
import android.support.v7.app.ActionBarActivity;

```

```
import android.os.Bundle;

```

```
import android.view.Menu;

```

```
import android.view.MenuItem;

```

```
import android.view.View;

```

```
import android.widget.Button;

```

```
import android.widget.ImageView;

```

```
import android.widget.PopupMenu;

```

```
import android.widget.TextView;

```

```
import android.widget.Toast;

```

```
public class MainActivity extends ActionBarActivity {

```

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_main);

```

```
Button button = (Button) findViewById(R.id.button);

```

```
TextView textView = (TextView) findViewById(R.id.textView);

```

```
ImageView imageView = (ImageView) findViewById(R.id.imageView);

```

```
button.setOnClickListener(viewClickListener);

```

```
textView.setOnClickListener(viewClickListener);

```

```
imageView.setOnClickListener(viewClickListener);}

```

```
View.OnClickListener viewClickListener = new View.OnClickListener() {

```

```
@Override

```

```
public void onClick(View v) {

```

```
showPopupMenu(v);} };

```

```
private void showPopupMenu(View v) {

```

```
PopupMenu popupMenu = new PopupMenu(this, v);

```

```
popupMenu.inflate(R.menu.popupmenu); // Для Android 4.0

```

```
// для версии Android 3.0 нужно использовать длинный вариант

```

```
// popupMenu.getMenuInflater().inflate(R.menu.popupmenu,

```

```
// popupMenu.getMenu());

```

```
popupMenu

```

```
.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {

```

```
@Override

```

```
public boolean onMenuItemClick(MenuItem item) {

```

```
// Toast.makeText(PopupMenuDemoActivity.this,

```

```
// item.toString(), Toast.LENGTH_LONG).show();

```

```
// return true;

```

```
switch (item.getItemId()) {

```

```
case R.id.menu1:

```

```
Toast.makeText(getApplicationContext(),

```

```
"Вы выбрали PopupMenu 1",

```

```
Toast.LENGTH_SHORT).show();

```

```
return true;

```

```
case R.id.menu2:

```

```
Toast.makeText(getApplicationContext(),

```

```
"Вы выбрали PopupMenu 2",

```

```
Toast.LENGTH_SHORT).show();

```

```
return true;

```

```
case R.id.menu3:

```

```
Toast.makeText(getApplicationContext(),

```

```
"Вы выбрали PopupMenu 3",

```

```
Toast.LENGTH_SHORT).show();

```

```
return true;

```

```
default:

```

```
return false;} } });

```

```
popupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {

```

```
@Override

```

```
public void onDismiss(PopupMenu menu) {

```

```
Toast.makeText(getApplicationContext(), "onDismiss",

```

```
Toast.LENGTH_SHORT).show();} });

```

```
popupMenu.show();} }

```

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

Запустив проект, вы можете щёлкать по любому элементу на форме и увидеть всплывающее меню, рис. 56.

#### **TOAST - ВСПЛЫВАЮЩИЕ СООБЩЕНИЯ**

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

Всплывающее уведомление (Toast Notification) является сообщением, которое появляется на поверхности окна приложения, заполняя необходимое ему количество пространства,

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

Для создания всплывающего уведомления необходимо инициализировать объект Toast при помощи метода Toast.makeText(), а затем вызвать метод show() для отображения сообщения на экране:

```
Toast toast = Toast.makeText(getApplicationContext(),

```

```
"Пора покормить кота!", Toast.LENGTH_SHORT);

```

```
toast.show();

```

У метода makeText() есть три параметра:

* Контекст приложения;
* Текстовое сообщение;
* Продолжительность времени показа уведомления. Можно использовать только две константы;

Константы для указания продолжительности показа сообщения:

* LENGTH\_SHORT — (По умолчанию) показывает текстовое уведомление на короткий промежуток времени;
* LENGTH\_LONG — показывает текстовое уведомление в течение длительного периода времени.

Если покопаться в исходниках Android, то можно найти такие строчки:

```
private static final int LONG_DELAY = 3500; // 3.5 seconds

```

```
private static final int SHORT_DELAY = 2000; // 2 seconds

```

Как видите, уведомления выводятся на 3 с половиной секунды или на 2 секунды. Других вариантов нет, не пытайтесь использовать другие значения - у вас ничего не получится.

По умолчанию стандартное всплывающее уведомление появляется в нижней части экрана. Изменить место появления уведомления можно с помощью метода setGravity(int, int, int). Метод принимает три параметра:

* стандартная константа для размещения объекта в пределах большего контейнера (например, GRAVITY.CENTER, GRAVITY.TOP и др.);
* смещение по оси X;
* смещение по оси Y.

Например, если вы хотите, чтобы уведомление появилось в центре экрана, то используйте следующий код:

```
toast.setGravity(Gravity.CENTER, 0, 0);

```

Если нужно сместить уведомление направо, то просто увеличьте значение второго параметра. Для смещения вниз нужно увеличить значение последнего параметра. Соответственно, для смещения вверх и влево используйте отрицательные значения.

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

Теперь поэкспериментируем. Создайте новый проект или используйте любой старый проект из предыдущих занятий. Добавьте на экран активности кнопку и присвойте ей текст Показать Toast, а также присвойте атрибуту android:onClick значение showToast. Теперь напишем код:

```
public void showToast(View view) {

```

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

```

```
Toast toast = Toast.makeText(getApplicationContext(),

```

```
"Пора покормить кота!",

```

```
Toast.LENGTH_SHORT);

```

```
toast.setGravity(Gravity.CENTER, 0, 0);

```

```
toast.show();}

```

Запустите приложение и нажмите кнопку. В центре экрана появится на короткое время текстовое сообщение, которое само исчезнет, рис. 58.

Для закрепления материала напишем еще один пример. Удалим предыдущий код для щелчка кнопки и напишем такой код:

```
int duration = Toast.LENGTH_LONG;

```

```
Toast toast2 = Toast.makeText(getApplicationContext(),

```

```
R.string.catfood,

```

```
duration);

```

```
toast2.setGravity(Gravity.TOP, 0, 0);

```

```
toast2.show();

```

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

Как правило, для Toast используются короткие текстовые сообщения. При необходимости вы можете добавить к сообщению и картинку. Используя метод setView(), принадлежащий объекту Toast, вы можете задать любое представление (включая разметку) для отображения.

Начнем с приготовлений. Подготовьте картинку и разместите её в папке res/drawable. Картинка будет доступна приложению как ресурс через название файла без расширения. Например, я добавил в папку файл с изображением кота hungrycat.jpg и могу получить к

нему доступ через выражение R.drawable.hungrycat. Чтобы изображение появилось в стандартном Toast-сообщении, нам потребуется программно создать объект класса ImageView и задать для него изображение из ресурсов с помощью методаsetImageResource. Сам по себе стандартный внешний вид Toast состоит из контейнера LinearLayout, в который нужно добавить созданный объект ImageView. Можно задать также позицию, в которую следует вывести изображение. Если указать значение 0, то изображение будет показано выше текста, рис. 59. Код для создания Toast с изображением выглядит следующим образом:

```
public void showToast(View view) {

```

```
Toast toast3 = Toast.makeText(getApplicationContext(),

```

```
R.string.catfood, Toast.LENGTH_LONG);

```

```
toast3.setGravity(Gravity.CENTER, 0, 0);

```

```
LinearLayout toastContainer = (LinearLayout) toast3.getView();

```

```
ImageView catImageView = new ImageView(getApplicationContext());

```

```
catImageView.setImageResource(R.drawable.hungrycat);

```

```
toastContainer.addView(catImageView, 0);

```

```
toast3.show();}

```

#### **УВЕДОМЛЕНИЯ**

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

Когда пользователь открывает расширенное сообщение, Android запускает объект Intent, который определён в соответствии с уведомлением. Можно также конфигурировать уведомление с добавлением звука, вибрации и мигающих индикаторов на мобильном устройстве.

Этот вид уведомления удобен в том случае, когда приложение работает в фоновом режиме и должно уведомить пользователя о каком-либо важном событии. Уведомление будет висеть до тех пор, пока пользователь не отреагирует на него, в отличие от Toast-сообщения, которое исчезнет через несколько секунд. Фоновое приложение создаёт уведомление в строке состояния, но не запускает активность самостоятельно для получения пользовательского взаимодействия. Это должен сделать только сам пользователь в удобное ему время.

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

* Notification — определяем свойства уведомления строки состояния: значок, расширенное сообщение и дополнительные параметры настройки (звук и др.)
* NotificationManager — системный сервис Android, который управляет всеми уведомлениями. Экземпляр NotificationManager создается при помощи вызова метода getSystemService(), а затем, когда надо показать уведомление пользователю, вызывается метод notify(). Недавно появился более простой способ через метод from()

Начиная с Android 3, для уведомлений используется класс Notification.Builder. Добавим на экран активности кнопку и создадим простой пример для демонстрации работы уведомления.

```
package ru.schoolbreaker.testapplication;

```

```
import android.app.Notification;

```

```
import android.app.NotificationManager;

```

```
import android.app.PendingIntent;

```

```
import android.content.Context;

```

```
import android.content.Intent;

```

```
import android.content.res.Resources;

```

```
import android.graphics.BitmapFactory;

```

```
import android.support.v7.app.ActionBarActivity;

```

```
import android.os.Bundle;

```

```
import android.view.Menu;

```

```
import android.view.MenuItem;

```

```
import android.view.View;

```

```
public class MainActivity extends ActionBarActivity {

```

```
// Идентификатор уведомления

```

```
private static final int NOTIFY_ID = 101;

```

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_main);}

```

```
public void onClick(View view) {

```

```
Context context = getApplicationContext();

```

```
Intent notificationIntent = new Intent(context, MainActivity.class);

```

```
PendingIntent contentIntent = PendingIntent.getActivity(context,

```

```
0, notificationIntent,

```

```
PendingIntent.FLAG_CANCEL_CURRENT);

```

```
Resources res = context.getResources();

```

```
Notification.Builder builder = new Notification.Builder(context);

```

```
builder.setContentIntent(contentIntent)

```

```
.setSmallIcon(R.drawable.ic_launcher_cat)

```

```
// большая картинка

```

```
.setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.hungrycat))

```

```
//.setTicker(res.getString(R.string.warning)) // текст в строке состояния

```

```
.setTicker("Последнее китайское предупреждение!")

```

```
.setWhen(System.currentTimeMillis())

```

```
.setAutoCancel(true)

```

```
//.setContentTitle(res.getString(R.string.notifytitle)) // Заголовок уведомления

```

```
.setContentTitle("Напоминание")

```

```
//.setContentText(res.getString(R.string.notifytext))

```

```
.setContentText("Пора покормить кота"); // Текст уведомления

```

```
// Notification notification = builder.getNotification(); // до API 16

```

```
Notification notification = builder.build();

```

```
NotificationManager notificationManager = (NotificationManager) context

```

```
.getSystemService(Context.NOTIFICATION_SERVICE);

```

```
notificationManager.notify(NOTIFY_ID, notification);} }

```

Для начала вам надо создать идентификатор уведомления. Он нужен, чтобы можно было различать уведомления друг от друга. Ведь вы можете создать идеальное приложение, которое уведомляло бы хозяина, что кота надо покормить (первое уведомление), погладить (второе уведомление), почистить лоток (третье уведомление). Если у вас будет один идентификатор, то каждое новое уведомление затрёт предыдущее и хозяин не увидит свои недоработки. Это не дело. Для идентификатора используйте какое-нибудь число.

Далее формируется внешний вид и поведение уведомления через построитель Notification.Builder. Вы можете задать текст уведомлений, значки и прочие атрибуты:

* Метод setSmallIcon() устанавливает маленький значок, который выводится в строке состояния, а также в правой части открытого уведомления.
* Метод setLargeIcon() устанавливает большой значок, который выводится в открытом уведомлении слева.
* Метод setTicker() выводит временную строку в строке состояния, которая затем исчезает. Остаётся только маленький значок (см. выше)

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

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

Начиная с API 16, вместо устаревшего метода getNotification() следует использовать метод build().

Далее надо сформировать уведомление с помощью специального менеджера. Ссылку на NotificationManager можно получить через вызов метода getSystemService(), передав ему в качестве параметра строковую константу NOTIFICATION\_SERVICE, определённую в классе Context

Выводится уведомление с помощью метода notify() - своеобразный аналог метода show() у Toast из предыдущего урока.

На первой картинке мы видим, как появился маленький значок. Также появляется текст "Последнее китайское предупреждение!", который быстро исчезает, рис. 60.

Далее мы можем открыть уведомление, чтобы увидеть более подробную информацию, рис. 61.

В примере показан избыточный код, чтобы вы сразу получили представление о возможностях уведомлений. Часть методов с префиксом set можно пропустить. Ниже показан укороченный код с использованием библиотеки совместимости для старых устройств. Обратите также внимание на упрощённый вариант доступа к менеджеру уведомлений через методNotificationManagerCompat.from(this).

```
Notification.Builder builder = new NotificationCompat.Builder(this);

```

```
// оставим только самое необходимое

```

```
builder.setContentIntent(contentIntent)

```

```
.setSmallIcon(R.drawable.ic_launcher_cat)

```

```
.setContentTitle("Напоминание")

```

```
.setContentText("Пора покормить кота"); // Текст уведомления

```

```
Notification notification = builder.build();

```

```
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);

```

```
notificationManager.notify(NOTIFY_ID, notification);

```

Как мы уже упоминали, если вам нужно обновить уведомление, то просто ещё раз отправьте его устройству под этим же идентификатором.

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

Совсем не обязательно запускать своё приложение, хотя это является распространённой

практикой. Можете задать нужное поведение, например, запустить свой сайт по указанному адресу. Переделаем код:

```
Context context = getApplicationContext();

```

```
Intent notificationIntent = new Intent(Intent.ACTION_VIEW,

```

```
Uri.parse("http://developer.schoolbreaker.ru/android/"));

```

```
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,

```

```
notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);

```

```
Notification.Builder builder = new Notification.Builder(context)

```

```
.setContentTitle("Посетите мой сайт")

```

```
.setContentText("http://developer.schoolbreaker.ru/android/")

```

```
.setTicker("Внимание!").setWhen(System.currentTimeMillis())

```

```
.setContentIntent(pendingIntent)

```

```
.setDefaults(Notification.DEFAULT_SOUND).setAutoCancel(true)

```

```
.setSmallIcon(R.drawable.ic_launcher);

```

```
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

```

```
notificationManager.notify(NOTIFY_ID, builder.build());

```

Обратите внимание, что на этот раз мы не указали картинку для большого значка и система подставляет в этом случае маленький значок, растягивая его до нужных размеров. Также появился новый метод setDefaults(), о котором говорится ниже.

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

```
setProgress(100, 50, false);

```

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

```
Intent intent = new Intent();

```

Вы можете из программы удалить своё уведомление, посланное по глупости.

```
notificationManager.cancel(NOTIFY_ID);

```

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

* Notification.DEFAULT\_LIGHTS
* Notification.DEFAULT\_SOUND
* Notification.DEFAULT\_VIBRATE

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

```
notification.defaults = Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE;

```

Если хотите установить сразу все значения по умолчанию, задействуйте константу Notification.DEFAULT\_ALL.

Использование звуковых оповещений для уведомления пользователя о событиях, связанных с устройством (например, входящий звонок), стало привычным. Большинство стандартных событий, от входящих звонков до новых сообщений и низкого заряда батареи, объявляются с помощью звуковых мелодий. Android позволяет проигрывать любой звуковой файл на телефоне в качестве уведомления. Чтобы это сделать, нужно присвоить свойству sound путь URI:

```
notification.sound = ringURI;

```

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

```
Uri ringURI = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

```

```
notification.sound = ringURI;

```

С SD-карты:

```
notification.sound = Uri.parse("file:///sdcard/cat.mp3");

```

```
// если знаем точный путь

```

Объект Notification включает в себя свойства для настройки цвета и частоты мерцания светодиодов устройства. Здесь стоит обратить внимание, что конкретные модели устройств могут не содержать светодиодные индикаторы или иметь другие цвета.

Свойство ledARGB может устанавливать цвет для светодиодной подсветки.

Свойства ledOffMS и ledOnMS позволяют регулировать частоту и поведение светодиодов. Вы можете включить светодиоды, присвоив свойству ledOnMS значение 1, а ledOffMS – 0. Присвоив им обоим значения 0, светодиоды можно выключить.

Настроив работу со светодиодами, необходимо также добавить флаг FLAG\_SHOW\_LIGHTS к свойству flags объекта Notification.

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

```
notification.ledARGB = Color.RED;

```

```
notification.ledOffMS = 0;

```

```
notification.ledOnMS = 1;

```

```
notification.flags = notification.flags | Notification.FLAG_SHOW_LIGHTS;

```

В настоящее время эмулятор Android не умеет визуально показывать активность светодиодов.

Вы можете делать уведомления текущими и/или настойчивыми, устанавливая флаги FLAG\_INSISTENT и FLAG\_ONGOING\_EVENT. Уведомления, помеченные как текущие, используются для представления событий, которые выполняются в данный момент времени (например, загрузка файла, фоновое проигрывание музыки). Текущие уведомления необходимы для сервисов, работающих на переднем плане. Пример установки флагов:

```
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;

```

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

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

```
notification.flags = notification.flags | Notification.FLAG_INSISTENT;

```

В методе getActivity() может понадобиться изменить флаг, например.

```
PendingIntent pendingIntent = PendingIntent.getActivity(context,0

```

```
,intent,Intent.FLAG_ACTIVITY_NEW_TASK);

```

Существуют и другие флаги. Хотя в большинстве случаев используется просто 0.

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

```
Intent intent = new Intent(context, MainActivity.class);

```

```
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);

```

Либо вы можете прописать в манифесте для нужной активности атрибут android:launchMode="singleTop".

#### **КОНВЕРТЕР "38 ПОПУГАЕВ"**

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

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

Создаём новый проект Converter и добавляем строковые ресурсы в файл res/values/strings.xml.

```
<?xml version="1.0" encoding="utf-8"?>

```

```
<resources>

```

```
<string name="action_settings">Settings</string>

```

```
<string name="app_name">38 попугаев</string>

```

```
<string name="radiobutton_meters">В метрах</string>

```

```
<string name="radiobutton_parrots">В попугаях</string>

```

```
<string name="buttonConvert_text">Конвертировать</string>

```

```
<color name="activity_color">#3399cc</color>

```

```
</resources>

```

Открываем файл activity\_main.xml в папке res/layout/ и настраиваем внешний вид экрана приложения. Удаляем глупую надпись Hello World!. Далее с панели инструментов из

раздела Text Fields перетаскиваем элемент Plain Text. Затем в разделе Containers находим элемент RadioGroup и также перетаскиваем на экран. Теперь из раздела Widgets дважды переносим элемент RadioButton и размещаем их внутри RadioGroup. Завершающий штрих - добавляем кнопку.

Мы подготовили каркас приложения. Теперь сделаем небольшой тюнинг. Назначим необходимые свойства добавленным элементам. Свойства можно менять через отдельную панель Properties.

Давайте сделаем так, чтобы при вводе текста в текстовом поле по умолчанию появлялась цифровая клавиатура. Так удобнее будет пользователю вводить длину кота. Выделяем компонент EditText, находим у него свойство InputType, раскрываем его и ставим флажки у свойств numberSigned и numberDecimal, рис. 62.

Далее присвоим текст переключателям RadioButton. Мы заранее уже заготовили строковые ресурсы для переключателей. Поэтому нам нужно просто назначить нужные ресурсы в свойствах Text: radiobutton\_meters и radiobutton\_parrots. Убедитесь, что у первого переключателя у свойства Checked установлено значениеtrue.

У кнопки для свойства Text мы используем строковый ресурс buttonConvert\_text, а для свойства onClick - onClick.

Чтобы приложение не выглядело стандартным, присвоим свойству background у корневого элемента значение activity\_color (цвет морской волны).

Чтобы приложение выглядело профессиональнее, изменим также идентификаторы по умолчанию на более понятные: editText, radioButtonMeter, radioButtonParrot, buttonConverter.

Побудьте дизайнером, придумайте свои варианты. У нас получилось следующее, рис. 63.

Код разметки выглядит следующим образом:

```
<RеlativeLаyout xmlns:android="http://schemas.android.com/apk/res/android"

```

```
хmlns:toоls="http://schemas.android.com/tools"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="match_parent"

```

```
android:background="@color/activity_color"

```

```
android:paddingBottom="@dimen/activity_vertical_margin"

```

```
android:paddingLeft="@dimen/activity_horizontal_margin"

```

```
android:paddingRight="@dimen/activity_horizontal_margin"

```

```
android:paddingTop="@dimen/activity_vertical_margin"

```

```
tоols:context=".MainActivity">

```

```
<ЕditТext

```

```
android:id="@+id/editText"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:layout_alignParentTop="true"

```

```
android:layout_centerHorizontal="true"

```

```
android:inputType="numberDecimal|numberSigned"/>

```

```
<RаdioGrоup

```

```
android:id="@+id/radioGroup"

```

```
android:layout_width="match_parent"

```

```
android:layout_height="wrap_content"

```

```
android:layout_alignParentStart="true"

```

```
android:layout_below="@+id/editText">

```

```
<RаdioВutton

```

```
android:id="@+id/radioButtonMeter"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:checked="true"

```

```
android:text="@string/radiobutton_meters"/>

```

```
<RаdioВutton

```

```
android:id="@+id/radioButtonParrot"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:text="@string/radiobutton_parrots"/>

```

```
</RаdioGrоup>

```

```
<Вutton

```

```
android:id="@+id/buttonConverter"

```

```
android:layout_width="wrap_content"

```

```
android:layout_height="wrap_content"

```

```
android:layout_below="@+id/radioGroup"

```

```
android:layout_centerHorizontal="true"

```

```
android:layout_marginTop="28dp"

```

```
android:onClick="onClick"

```

```
android:text="@string/buttonConvert_text"/>

```

```
</RеlativeLayout>

```

Теперь будем писать код. Открываем файл активности MainActivity.java. Нам понадобятся два метода для конвертации величин и обработчик щелчка кнопки. При пустом значении текстового поля будем выводить Toast-сообщение. Начнём с двух методов.

Методы просты. Указываем количество попугаев и делим на магическое число 7.6 - получаем метры. И наоборот, указываем число метров и получим количество попугаев.

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

```
package ru.schoolbreakes.converter;

```

```
import android.support.v7.app.ActionBarActivity;

```

```
import android.os.Bundle;

```

```
import android.view.Menu;

```

```
import android.view.MenuItem;

```

```
import android.view.View;

```

```
import android.widget.EditText;

```

```
import android.widget.RadioButton;

```

```
import android.widget.Toast;

```

```
public class MainActivity extends ActionBarActivity {

```

```
private EditText mInputEditText;

```

```
@Override

```

```
protected void onCreate(Bundle savedInstanceState) {

```

```
super.onCreate(savedInstanceState);

```

```
setContentView(R.layout.activity_main);

```

```
mInputEditText = (EditText) findViewById(R.id.editText);}

```

```
// Конвертируем в метры

```

```
private float convertParrotToMeter(float parrot) {

```

```
return (float) (parrot / 7.6);}

```

```
// Конвертируем в попугаи

```

```
private float convertMeterToParrot(float meter) {

```

```
return (float) (meter * 7.6);}

```

```
public void onClick(View view){

```

```
switch (view.getId()) {

```

```
case R.id.buttonConverter:

```

```
RadioButton meterRadioButton = (RadioButton) findViewById(R.id.radioButtonMeter);

```

```
if (mInputEditText.getText().length() == 0) {

```

```
Toast.makeText(getApplicationContext(), "Введите длину кота",

```

```
Toast.LENGTH_LONG).show();

```

```
return;}

```

```
float inputValue = Float.parseFloat(mInputEditText.getText().toString());

```

```
if (meterRadioButton.isChecked()) {

```

```
mInputEditText.setText(String

```

```
.valueOf(convertParrotToMeter(inputValue)));

```

```
} else {

```

```
mInputEditText.setText(String

```

```
.valueOf(convertMeterToParrot(inputValue)));}

```

```
break;} } }

```

Запускаем проект и любуемся новым приложением, рис. 64.

Фактически, нам достаточно, кроме отображаемого в интерфейсе текста, поменять содержимое методов с префиксом convert.