четверг, 17 февраля 2011 г.

на Java под Android: быстро и с пользой

В процессе изучения программирования под платформу Android я прочитал десяток статей в стиле "классный HelloWorld с кнопкой за 15 минут". Хочется разбавить это многообразие своим примером. Но на этот раз сделаем что-то более полезное.
Для начала определимся с идеей. Я побродил по Android Market-у и нашёл несколько достаточно популярных приложений для поиска вакансий. И ни одно из них не предлагает вакансии в Украине. Осталось взять сайт поприличнее и использовать его rss как источник данных для приложения. Я остановился на work.ua, но можно будет добавить ещё несколько источников для большего охвата рынка и независимости.

1. Собираем окружение

Самый продвинутый плагин для работы с Android SDK на сегодня, безусловно, под Eclipse. Но и меня, как последовательного сторонника NetBeans не обделили: есть неплохой плагин nbandroid, которым и воспользуемся. Скачиваем SDK с официального сайта платформы. После распаковки SDK, (делаем всё под Ubunu, естественно) запускаем бинарник android из tools, который отображает интерфейс для установки различных версий платформы, создания виртуальных устройств и т.п. Из этой же директории пригодится avd - запуск и управление виртуальными девайсами и ddms - визуальный инструмент для отладки приложений на этих самых девайсах. Всё устанавливаем, создаём пустой проект.

2. Структура проекта

SDK представляет очень развитую модель классов, так что писать под него удивительно просто. Точкой входа в приложение является ваш класс, наследующий Activity. Можно создать их несколько, переключая "окна" по мере совершения действий пользователем, а можно просто перерисовывать контент одного Activity сколько угодно и как угодно в рантайме. Второй вариант мне почему-то показался интереснее. Настройки приложения задаём в единственном xml-файле AndroidManifest.xml. Главное, что прийдётся модифицировать в автоматически сгенерированном плагином файле: добавлять по необходимости uses-permission (требуемые приложению доступы) и править атрибуты тегов application (свойства приложения) и activity (свойства нашего "экрана").  Добавляем наши классы: UIFactory - будет содержать методы для генерации View - визуальных элементов интерфейса и пакет со "слушателями", в которых мы реализуем логику обработки действий пользователя в разных "окнах" приложения. "Окон" у нас будет три: главное окно с формой для установки условий поиска вакансий, окно со списком вакансий, и окно с подробной информацией о конкретной вакансии. Следовательно и "слушателя" сделаем три: MainFormListener (реализуем OnClickListener, помещая логику в метод onClick),  ListFormListener (реализуем OnClickListener и OnItemClickListener) и DetailsFormListener (реализуем OnClickListener). Парсинг rss и обёртку объекта "Вакансия" реализуем в классах ещё одного пакета.

3. XML или Java

Разработчики SDK предусмотрели удобное xml-представление интерфейса: все объекты (наследники View) могут быть описаны в xml ресурсах. Например:


  1. <EditText android:id="@+id/commentText"
  2.         android:layout_width="fill_parent"
  3.         android:layout_height="wrap_content"/>

аналогично в java-коде:


  1. new EditText(this).setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

Как видим, подходы вполне равносильны. Можно похоливарить: отладка в java-коде проще, читаемость xml выше и т.д... Но на практике всё равно каждый выбирает что удобнее. Мне удобнее java. В UIFactory делаем методы, которые принимают Activity и возвращают View. Различные элементы интерфейса "вкладываем" друг в друга методом parent.addView(View child). Например, метод построения главной формы:


  1.     public static View getMainWindow(Activity act) {
  2.         LinearLayout ll = new LinearLayout(act);
  3.         ll.setOrientation(LinearLayout.VERTICAL);
  4.  
  5.         TextView t1 = new TextView(act);
  6.         t1.setText("Категория:");
  7.         t1.setTextSize(18);
  8.         ll.addView(t1);
  9.  
  10.         Spinner cats = new Spinner(act);
  11.         cats.setId(MAIN_CATEGORY);
  12.         cats.setAdapter(new ArrayAdapter<String>(act, android.R.layout.simple_spinner_item, DataFactory.getCategories()));
  13.         ll.addView(cats);
  14.  
  15.         TextView t2 = new TextView(act);
  16.         t2.setText("Регион:");
  17.         t2.setTextSize(18);
  18.         ll.addView(t2);
  19.  
  20.         Spinner regions = new Spinner(act);
  21.         regions.setId(MAIN_LOCATION);
  22.         regions.setAdapter(new ArrayAdapter<String>(act, android.R.layout.simple_spinner_item, DataFactory.getLocations()));
  23.         ll.addView(regions);
  24.  
  25.         Button button = new Button(act);
  26.         button.setOnClickListener(new MainFormListener(act));
  27.         button.setText("Поиск");
  28.         ll.addView(button);
  29.  
  30.         return ll;
  31.     }
Результат этих "строителей" отображаем на экране методом setContentView(Vew form) нашего Activity. Если очередному "строителю" для построения View понадобятся динамические данные, соответствующий метод UIFactory будет кроме Activity принимать ещё и соответствующий массив. Первый setContentView (отрисовка первой формы) делаем непосредственно в Activity в переопределённом методе onCreate. Остальные - в методах соответствующих "слушателей".

4. Обработка событий

Тут всё совершенно привычно и крайне удобно. У "активных" View (кнопок, списков и т.п.) есть методы set...что-то..Lstener(Listener l), куда передаём объект соответствующего слушателя. Классы-слушатели реализуют один или несколько интерфейсов и соответственно один или несколько методов, каждый из которых вызывается системой при наступлении соответствующего события. Типичный метод Listener-а:


  1.     public void onClick(View arg0) {
  2.         act.setContentView(UIFactory.getMainWindow(act));
  3.     }
где act - объект Activity на котором всё отображаем, передан в Listener в конструкторе.