Expertus metuit
Программирование для Android, часть четвёртая: код и ресурсы пакета
2012-02-14 19:36

Java-классы проекта, макеты интерфейсов, ресурсы, доступ к ресурсам из кода

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

Запускаем eclipse командой eclipse-adt, там должен уже быть проект, который мы создали в прошлой части — first project, также запускаем эмулятор с виртуальным девайсом с Android 2.3.3.

Замечание Я позволю себе не вдаваться в детальные подробности, как именно открывать файлы в редакторе Eclipse, в прошлой части я рассказывал про вьюшки и в частности про Package explorer, также можно открывать файлы через диалог поиска eclipse-ресурсов, по умолчанию он висит на хоткее Ctrl+Shift+R, в нём вы можете просто набрать часть имени нужного файла, а потом стрелочками выбрать подходящий из списка, очень удобно.

Java-код

Итак, открываем в редакторе единственный пока java-файл нашего проекта — FirstprojectActivity.java, он был автоматически сгенерён при создании проекта и выглядит примерно так:

package com.example.firstProject;

import android.app.Activity;
import android.os.Bundle;

public class FirstprojectActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Рассмотрим этот файл построчно.

package com.example.firstProject;

В этой строчке мы видим объявление пакета (package), пакет — это сгруппированный набор объектов, в названии пакета используется схема с точками, каждый элемент названия (т.е. между точками) соответствует каталогу в проекте. В нашем случае файл FirstprojectActivity.java находится в каталоге (относительно каталога src) com/example/firstProject, все файлы (и классы в них) внутри этого же каталога также будут находиться в пакете com.example.firstProject.

Вы, наверное, заметили, что название пакета напоминает доменное имя, записанное в обратном порядке. Так вот это именно так и есть! Такой способ является в Java общепринятым способом именования пакетов и библиотек, чтобы каждый использовал свой собственный DNS-домен как идентификатор своих пакетов и не пересекался с другими.

import android.app.Activity;
import android.os.Bundle;

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

public class FirstprojectActivity extends Activity {

Это обычное наследование, мы наследуем класс FirstprojectActivity от класса Activity из пакета android.app.

    @Override
    public void onCreate(Bundle savedInstanceState) {

Здесь мы определяем метод onCreate, по его названию можно догадаться, что он вызывается при создании чего-то. Ключевое слово @Override говорит компилятору, что мы собираемся переопределять метод из родительского класса, если такого метода в родительском классе нет, мы получим ошибку в редакторе и компиляторе.

        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

Тут тоже всё просто: сначала вызываем этот метод из родительского класса, а затем — метод setContentView с параметром R.layout.main. Но где же начинается наша программа, если во всём проекте всего один класс с произвольным именем и странным методом?

Операции

Вам, возможно, знакома фраза «точка входа в программу», так обычно называет фрагмент кода, с которого начинается выполнение программы. Точкой входа может быть функция (например, функция main() в C/C++), класс (например, в Java-программах). Однако у андроидных приложений НЕТ точки входа в таком понимании. Андроидное приложение (если рассматривать APK-пакет как одно цельное приложение) по сути является коллекцией компонентов-микроприложений, а какое из них будет «запущено», определяется контекстом.

Всего существует несколько типов таких компонентов, один из них вы уже увидели: наш класс com.example.firstProject.FirstprojectActivity (а это его полное имя, вместе с содержащим его пакетом) является операцией (по-английски activity), так как отнаследован от класса android.app.Activity. Именно в операции реализуется пользовательский интерфейс, грубо говоря, каждому «окну» приложения соответсвует одна операция. Когда система создаёт пользовательский интерфейс, она сначала конструирует объект на основе соответствующего класса операции, а затем вызывает метод onCreate(), где приложение должно сформировать элементы интерфейса, запустить сетевые запросы, короче, инициализировать «окно». Приложение обязано в своём манифесте (файле AndroidManifest.xml) определить все свои операции, это делается в секции <activity>; если вы сейчас заглянете в манифесте нашего приложения, то увидите там примерно такое:

<activity
    android:name=".FirstprojectActivity"
    android:label="@string/app_name" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

Для каждой операции создаётся отдельный элемент <activity>. По его внешнему виду примерно понятно, что там за атрибуты и какие у них значения. В android:name находится название класса, в котором операция реализована (точка в начале имени означает, что имя относительное, т.е. должно находиться внутри главного пакета приложения, имя которого задано в атрибуте package элемента <manifest>). В android:label находится человекочитаемое название операции, которое показывается пользователю; в нашем примере вы видите внутри значение @string/app_name, это означает, что непосредтсвенное значение лейбла берётся из строковых ресурсов приложения, о них чуть ниже.

Внутри элемента <activity> находится элемент <intent-filter>, он определяет условия-интенты, при которых операция будет вызвана.

Интенты

Интент является ключевым элементом всей андроидной платформы, это слово очевидным образом произошло от английского intent, что в переводе значит «цель» или «намерение». Интент является запросом, который посылает приложение (или какой-нибудь другой компонент системы) с целью выполнить какое-нибудь действие. Например, если вы тыкаете по ссылке на веб-страницу внутри твиттер-клиента, приложение посылает запрос-интент в систему «Мне нужно открыть URL http://example.com». Система принимает этот запрос и дальше смотрит, заявило ли какое-нибудь приложение готовность принимать URL, если такое приложение нашлось, то ему перенаправляется запрос. Если у вас в системе несколько веб-браузеров, вы наверняка видели такую менюшку со списком браузеров — все они объявили, что принимают запросы на открытие веб-страниц.

Когда приложение или система посылают интент, они указывают действие (по-английски action, например, если хотят открыть веб-страницу в браузере, android.content.Intent.ACTION_VIEW) и (опционально) данные (по-английски data, например, URL веб-страницы http://google.com). Другой пример действия для интента, это — android.intent.action.MAIN, оно обозначает нечто вроде действия по умолчанию, аналог точки входа в операцию и обычно вызывается, когда вы тыкаете по иконке приложения на экране.

Приложение объявляет о своей готовности принимать интенты в манифесте, в элементе <intent-filter>, в нём должен содержаться обязательный элемент <action>, где указывается строка с действием, в нашем примере это строка android.intent.action.MAIN. Также в элементе <intent-filter> может находиться элемент <category> (в нём определяется категория интент-фильтра, например, чтобы в общем списке приложений появилась иконка, нужно указать категорию android.intent.category.LAUNCHER, а если этого не сделать, то своё приложение вы банально на телефоне не найдёте) и data (здесь указывается тип Uri, на которые будет реагировать этот фильтр).

Существует стандартный набор действий для интентов, предназначенный для базовых телефонно-планшетных операций, например, действие для запуска приложения-телефона — android.intent.action.DIAL.

Представление

Представление (по-английски view) — это такой своеобразный «кирпичик», из которых состоит пользовательский интерфейс приложения. Представление с точки зрения программы является классом, наследником класса android.view.View. Примеры стандартных представлений: Button, CheckBox, EditText и прочие интерфейсные элементы управления.

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

Ресурсы

Ресурсы приложения предназначены для отделения всяких визуальных хреновин (картинок, представлений и даже текстовых строк) от кода программы. В ресурсах хранятся, например, элементы оформления приложения для разных размеров и разрешений экрана; там же хранятся текстовые строки для разных языков интерфейса.

В дереве проекта в eclipse все ресурсы хранятся в папке res на верхнем уровне проекта (рядом с папкой src). Если вы развернёте все подпапки в res, то увидите примерно такое:

Дерево ресурсов приложения

В папках drawable-hdpi, drawable-ldpi, drawable-mdpi хранятся картинки для разных разрешений экрана. В папке layout хранятся макеты интерфейсов, а в папке values пока лежит только файл strings.xml, в котором хранятся, в частности, все интерфейсные строки.

Давайте для начала откроем файл strings.xml и посмотрим, что там внутри (сразу же переключимся на XML-вид, смотрите на табы в низу редактора):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, FirstprojectActivity!</string>
    <string name="app_name">First project</string>
</resources>

Строки, определяемые в этом файле, автоматически становятся доступны по всему приложению, их даже можно использовать внутри манифеста (вспомните определение операции, там в атрибуте android:label стояла ссылка на строку в виде @string/app_name). Такой файл можно создавать для каждого поддерживаемого человеческого языка, при этом ссылки на эти строки будут автоматически конвертироваться в нужный язык, очень удобно.

Давайте поменяем строку с именем hello на что-нибудь другое, например, так:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">And yet it moves!</string>
    <string name="app_name">First project</string>
</resources>

Сохраним файл и запустим проект на выполнение, на экране виртуального девайса увидим вот такое:

Изменённая строка на экране девайса

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

setContentView(R.layout.main);

Обратите внимание на конструкцию R.layout.main, это одна из замечательных фич андроида: все ресурсы проекта после компиляции становятся доступны как элементы базового класса R. А в Eclipse ADT они же доступны прямо в процессе написания кода, сразу же после создания. В данном случае макет интерфейса был преобразован в объект класса View и в таком виде стал доступен во время исполнения. ADT контролирует доступность ресурсов, то есть вы не сможете использовать ресурс, который ещё не создан — в редакторе это место будет помечено ошибкой.

Откроем файл res/layout/main.xml в редакторе, видим уже привычный вид: в центральной области графическое представление редактируемого файла, внизу строка с двумя табами — Graphical Layout и main.xml, где мы можем переключаться между графическим видом макета и его XML-кодом.

Редактор интерфейсов самый обычный: слева набор доступных интерфейсных элементов, в середине макет, куда эти элементы можно таскать мышкой, этой же мышкой можно менять расположение элементов. Обратите внимание на надпись на макете — And yet it moves! — она автоматически подгрузилась из строковых ресурсов в уже изменённом виде.

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

И под конец этой главы давайте поиграемся с локализациями: добавим поддержку русского языка. Для этого создадим в папке res новую папку с названием values-ru, для этого кликните правой кнопкой мыши по папке res и выберите в меню NewFolder, в поле Folder name введите values-ru и нажмите кнопку Finish. Теперь скопируйте в эту папку файл strings.xml из папки res/values (это можно сделать через контекстное меню: клик правой кнопкой мыши по res/values/strings.xml, Copy, клик правой кнопкой по res/values-ru, Paste). Откройте файл res/values-ru/strings.xml в редакторе и наберите там что-нибудь по-русски, например, так:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">И всё-таки она вертится!</string>
    <string name="app_name">Первый проект</string>
</resources>

Сохраните файл и запустите проект, в виртуальном девайсе должно открыться приложение, в нём все надписи на английском. Теперь переключитесь внутри девайса на русский язык (если забыли, это делается так: кнопка Home, кнопка Menu, SettingsLanguage and keyboardSelect languageРусский) и снова запустите приложение из Eclipse. Ура! Должно получиться так:

Первое локализованное приложение

Ссылки

← предыдущая часть

Комментарии

Алексей | 2012-04-13 в 23:40

Спасибо. У вас очень хороший стиль и все понятно. Пишите дальше.

tanatonaut | 2013-10-17 в 17:58

Чувак Чувак, пиши еще! Всё понятно и это главное!

Евгений | 2014-02-11 в 14:09

Хех (( жаль что нет продолжения.

Рома | 2014-06-14 в 06:47

Все супер...

андрей | 2015-03-19 в 22:54

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

Текст комментария (разметка: *курсив*, **полужирная**, [ссылка](http://example.com) или <http://example.com> ещё)
Имя (обязательно, 50 символов или меньше)
Email, на который получать ответы (не будет опубликован)
Веб-сайт
© 2006—2016 Sergey Stolyarov | Работает на Pyrone