среда, 16 июня 2010 г.

Pragmatic Android, part #1

Есть несколько обьектов в Android SDK, с которыми разработчик будет плотно и перманентно работать: activities, intents, services и content providers.

Я в шоке, это надо же было выбрать такие термины, которые не переводятся на русский нормально.. “настроим нашу активность” – ну это же бред (говорить с придыханием, © не мой)

Ну фиг с ними, с терминами, ладно уж..

Activity: это экран и элементы интерфейса на нем; я про себя называю это “форма” – привычка из делфи, все лучше, чем “активность”.. Приложение создает одну или несколько форм для всяческих своих нужд, форма должна позаботится о сохранности данных, с которыми она работает, если вдруг она вытесняется на задний план (где она может быть “убита” системой в случае нехватки ресурсов – и сделать это “без обьявления войны”)

Intents: “намерение, умысел”, еще один дурацкий термин.. намерение – это механизм описания специфического действия, например “отправка почты”: если я хочу из своего приложения отправить письмо, я должен обьявить о намерении сделать это; с другой стороны, я могу написать почтовый клиент и зарегистрировать его в системе как приложение, способное выполнить намерение “отправить почту”. Когда кто-то захочет отправить почту, ему будет предложен список программ, способных на это – в т.ч. и моя

Services: наконец нормальный термин. Сервис – это задача, которая выполняется фоново без участия/взаимодействия пользователя. Пример – мр3-плеер: скажем я хочу слушать музыку и читать книжку, если бы плеер был “обычной” программой, то при переключении на читалку музыка остановилась бы. Но если сделать сервис, который будет играть музыку – это решает проблему: некая программа взаимодействует с сервисом и дает ему список файлов для проигрывания и команду начать проигрывание; после этого сервис в фоне играет музыку, я переключаюсь на читалку, а музыка играет; если я захочу ее остановить или переключить трек – я вызову программу-плеер, которая даст соотв. команду сервису

Content Providers: контент-провайдер реализует api для работы с данными; приложения ничего не знают друг о друге, вот для обмена данными и используются контент-провайдеры – скажем, чтобы получить данные о контактах

Это имхо вся теория, которая нужна; а если нет, то я не виноват. Дальше практика – автор книги (и я за ним) шаг за шагом покажет, как написать игру Судоку для андроида (и может быть я ее наконец хоть раз решу)

Написание почти любой программы состоит из 2-х частей: программа должна как-то выглядеть (дизайн) и программа должна что-то делать (код)

Что касается дизайна, то лучше делфи конечно никто ничего не придумал (а самый запутанный способ рисования интерфейсов – имхо в маке с их х-кодом и интерфейс-билдером.. хотя это наверное просто смотря с чем сравнивать); для андроида вроде тоже есть какие-то wysiwyg-тулзы для проектирования интерфейса, но я так понимаю, что это поделки-самоделки, которыми пользуются чуть-ли не только сами авторы.. хотя я не спец и могу сильно ошибаться

В любом случае это отступление; интерфейс может быть создан в коде или с использованием декларативного метода, Google сильно советует последний способ (кроме очевидного соображения, что такой способ намного проще “читается”). Способ заключается в следующем: форма описывается xml-файлом, в котором указывается, какие контролы как расположены. Затем при компиляции вызывается компилятор ресурсов, который на основе xml-файлов создаст специальные данные, которые и будут использоваться приложением

Все, я начинаю что-то наконец делать: создаю в eclipse новый проект:

Screenshot - 16.06

Жму Finish, попадаю в eclipse и после небольшой паузы (среда прекомпилирует какие-то ресурсы) могу запустить свой первый проект – Run –> Run as –> Android Application; запустится эмулятор и через какое-то время (запускается он не мгновенно) я получу “виртуальный” телефон, разлочив который увижу свою программу:

Screenshot - 16.06Эмулятор ведет себя как настоящий телефон и закрыть его – значит выключить телефон; а включить любой телефон занимает какое-то время (все такие операционная система стартует), так что лучшие собаководы рекомендуют: эмулятор запускается утром и выключается вечером.

При создании нового проекта автоматически была добавлена единственная форма, которую и видим на экране; логично изменить ее так, чтобы мы увидели привычный для игр интерфейс – кнопки New Game и Exit по крайней мере. Пока же при запуске инифиализируется “форма по умолчанию”, которая заполняет собой экран, делается это в переопределенном методе OnCreate:

   1: package org.example.sudoku;
   2:  
   3: import android.app.Activity;
   4: import android.os.Bundle;
   5:  
   6: public class Sudoku extends Activity {
   7:     /** Called when the activity is first created. */
   8:     @Override
   9:     public void onCreate(Bundle savedInstanceState) {
  10:         super.onCreate(savedInstanceState);
  11:         setContentView(R.layout.main);
  12:     }
  13: }

Если посмотреть на файл AndroidManifest.xml (а этот файл обязательно должен быть в каждой android-программе), то увидим:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <manifest xmlns:android="http://schemas.android.com/apk/res/android"
   3:       package="org.example.sudoku"
   4:       android:versionCode="1"
   5:       android:versionName="1.0">
   6:     <application android:icon="@drawable/icon" android:label="@string/app_name">
   7:         <activity android:name=".Sudoku"
   8:                   android:label="@string/app_name">
   9:             <intent-filter>
  10:                 <action android:name="android.intent.action.MAIN" />
  11:                 <category android:name="android.intent.category.LAUNCHER" />
  12:             </intent-filter>
  13:         </activity>
  14:  
  15:     </application>
  16:     <uses-sdk android:minSdkVersion="7" />
  17:  
  18: </manifest> 

Здесь перечислены все (пока одна) формы (activities), принадлежащие приложению, и намерения, которые они готовы реализовать – так что при запуске показывается форма Sudoku

Ее дизайн описан в файле main.xml, который можно найти в разделе Sudoku/res/layout в списке проектов в eclipse; вообще в эту “папку” кладуться xml-файлы, которые описывают внешний вид форм; в OnCreate вызывается родительский OnCreate, а потом указывается, какую форму использовать: setContentView(R.layout.main). R - это класс, который создается и управляется автоматически средой разработки (android-плагином для eclipse), в него автоматически добавляются (и удаляются) идентификаторы ресурсов проекта (а ресурсы – это все, кроме кода – xml-файлы с описанием форм, xml-файлы со строчными ресурсами, картинки, звуки и т.д.)

Открываем main.xml:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   3:     android:orientation="vertical"
   4:     android:layout_width="fill_parent"
   5:     android:layout_height="fill_parent"
   6:     >
   7: <TextView  
   8:     android:layout_width="fill_parent" 
   9:     android:layout_height="wrap_content" 
  10:     android:text="@string/hello"
  11:     />
  12: </LinearLayout>

Разбираться с xml-форматом я не буду (я его ненавижу и не всегда понимаю, и уж точно не понимаю, как кому-то может нравится с ним что-то делать); этот файл описывает Layout (планировку) формы, планировка может быть нескольких видов:

  • FrameLayout: дочерние элементы располагаются из левого верхнего угла экрана
  • LinearLayout: дочерние элементы выстраиваются в одинарный столбец (используется чаще всего)
  • RelativeLayout: относительное расположение дочерних элементов друг к другу и к родителю
  • TableLayout: табличное размещение элементов

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

Модифицируем стартовую форму:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   3:     android:orientation="vertical"
   4:     android:layout_width="fill_parent"
   5:     android:layout_height="fill_parent">
   6:     <TextView
   7:         android:layout_width="fill_parent"
   8:         android:layout_height="wrap_content"
   9:         android:text="@string/main_title" />
  10:     <Button
  11:         android:layout_width="fill_parent"
  12:         android:layout_height="wrap_content"
  13:         android:text="@string/continue_label" />
  14:     <Button
  15:         android:layout_width="fill_parent"
  16:         android:layout_height="wrap_content"
  17:         android:text="@string/new_game_label" />
  18:     <Button
  19:         android:layout_width="fill_parent"
  20:         android:layout_height="wrap_content"
  21:         android:text="@string/about_label" />
  22:     <Button
  23:         android:layout_width="fill_parent"
  24:         android:layout_height="wrap_content"
  25:         android:text="@string/exit_label" />
  26: </LinearLayout>

и файл строковых ресурсов strings.xml:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <resources>    
   3:         <string name="app_name">Sudoku</string>
   4:         <string name="main_title">Android Sudoku</string>
   5:         <string name="continue_label">Continue</string>
   6:         <string name="new_game_label">New Game</string>
   7:         <string name="about_label">About</string>
   8:         <string name="exit_label">Exit</string>    
   9: </resources>

После чего смотрим результат:

scr-16.06 Можно покастомизировать еще немного: сделать фон окна другим, для этого сначала надо определить ресурс для этого – добавить в /res/values файл colors.xml и написать туда:

 

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <resources>
   3:   <color name="background">#3500ffff</color>
   4: </resources>

теперь можно использовать:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   3:     android:background="@color/background"
   4:     android:layout_height="fill_parent"
   5:     android:layout_width="fill_parent"
   6:     android:padding="30dip"
   7:     android:orientation="horizontal">
   8:     <LinearLayout
   9:         android:orientation="vertical"
  10:         android:layout_height="wrap_content"
  11:         android:layout_width="fill_parent"
  12:         android:layout_gravity="center">
  13:         <TextView
  14:             android:text="@string/main_title"
  15:             android:layout_height="wrap_content"
  16:             android:layout_width="wrap_content"
  17:             android:layout_gravity="center"
  18:             android:layout_marginBottom="25dip"
  19:             android:textSize="24.5sp" />
  20:         <Button
  21:             android:id="@+id/continue_button"
  22:             android:layout_width="fill_parent"
  23:             android:layout_height="wrap_content"
  24:             android:text="@string/continue_label" />
  25:         <Button
  26:             android:id="@+id/new_button"
  27:             android:layout_width="fill_parent"
  28:             android:layout_height="wrap_content"
  29:             android:text="@string/new_game_label" />
  30:         <Button
  31:             android:id="@+id/about_button"
  32:             android:layout_width="fill_parent"
  33:             android:layout_height="wrap_content"
  34:             android:text="@string/about_label" />
  35:         <Button
  36:             android:id="@+id/exit_button"
  37:             android:layout_width="fill_parent"
  38:             android:layout_height="wrap_content"
  39:             android:text="@string/exit_label" />
  40:     </LinearLayout>
  41: </LinearLayout>

Кроме того, что указан цвет для фона (android:background="@color/background"), изменилось расположение элементов: создается LinearLayout (линейная планировка), с высотой “по содержимому”, внутри которой текст и кнопки; у кнопок добавились идентификаторы (android:id="@+id/new_button"), по которым можно будет легко обращаться в дальнейшем (т.е. кнопка New Game будет искаться как по идентификатору R.id.new_button); добавились модификаторы размеров – размер текста и отступы

Размеры указываются в аппаратно-независимых пикселах (dp/dip) и масштабо-независимых пикселах (sp) – т.е. отступ в 30 dp будет одинаково выглядеть на разных телефонах – и с размером экрана 480 точек в высоту, и 800; dp используется для “расстояний”, а sp – для размера шрифтов

И уже этот элемент (который “линейная планировка”.. лучше наверное “линейный вид”) размещен еще в одном виде, занимающем все окно:

scr-16.062Следующая фишка: а если повернуть телефон в ландшафтный режим (на эмуляторе это делается Ctrl-F11 или клавишами Num-7 – Num-9 (с отключенным NumLock))? Хорошего мало, нижняя кнопка не влезает :(

Решается просто: в папке res создается папка layout-land – интуитивно понятно, что для ландшафтного режима описание вида будет браться отсюда; создаем в ней файл main.xml:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <LinearLayout
   3:     xmlns:android="http://schemas.android.com/apk/res/android"
   4:     android:background="@color/background"
   5:     android:layout_height="fill_parent"
   6:     android:layout_width="fill_parent"
   7:     android:padding="15dip"
   8:     android:orientation="horizontal">
   9:         <LinearLayout
  10:             android:orientation="vertical"
  11:             android:layout_height="wrap_content"
  12:             android:layout_width="fill_parent"
  13:             android:layout_gravity="center"
  14:             android:paddingLeft="20dip"
  15:             android:paddingRight="20dip">
  16:             <TextView
  17:                 android:text="@string/main_title"
  18:                 android:layout_height="wrap_content"
  19:                 android:layout_width="wrap_content"
  20:                 android:layout_gravity="center"
  21:                 android:layout_marginBottom="20dip"
  22:                 android:textSize="24.5sp" />
  23:             <TableLayout android:layout_height="wrap_content"
  24:                 android:layout_width="wrap_content"
  25:                 android:layout_gravity="center"
  26:                 android:stretchColumns="*">
  27:                 <TableRow>
  28:                     <Button
  29:                         android:id="@+id/continue_button"
  30:                         android:text="@string/continue_label" />
  31:                     <Button
  32:                         android:id="@+id/new_button"
  33:                         android:text="@string/new_game_label" />
  34:                 </TableRow>
  35:                 <TableRow>
  36:                     <Button
  37:                         android:id="@+id/about_button"
  38:                         android:text="@string/about_label" />
  39:                     <Button
  40:                         android:id="@+id/exit_button"
  41:                         android:text="@string/exit_label" />
  42:                 </TableRow>
  43:             </TableLayout>
  44:         </LinearLayout>
  45: </LinearLayout>

Линейный вид на все окно, внутри которого – текстовка и табличный вид, внутри которого – 2 столбца с кнопками:

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

Я устал писать…

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

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