Пишем простое приложение с использованием GoogleMap и прокачанным SimpleCursorAdapter
Разработка приложений под платформу Android становится все более и более массовой. Так, мне недавно довелось заниматься разработкой клиентской части такого приложения, фактически с нуля разбираясь в премудростях этого дела, опираясь лишь на базовые знания Java.
Здесь на примере простого приложения, которое позволяет выбрать станцию метро из списка и отобразить её на карте, показаны некоторые полезные фичи работы с адаптером для ListView, реализованы простейшая работа гуглокарты и взаимодействие с встроенной БД. Весь проект можно скачать на github по ссылке в конце статьи.
Собственно, так выглядит список:
Он включает в себя сам список станций и строку поиска по станциям. В поиске реализована подсказка — при наборе последовательности букв в списке остаются только те станции, название которых начинается с этой последовательности. Тапаем по станции Белорусская, получаем:
Теперь посмотрим, что же там происходит. Нетрудно заметить, что здесь используется гуглокарта (MapView). Соответственно, важно помнить, что в качестве target при создании проекта нужно указать Google APIs.
Теперь по пунктам:
1. AndroidManifestЭто базовый файл, в котором описываются настройки приложения и указывается, к чему оно имеет доступ. Соответственно, в этом файле необходимо прописать доступ к интернету, указать, что используется библиотека com.google.android.maps, и указать наши активити: в данном случае SearchDialog, которое появляется при запуске, и Map — активити с картой.
2. База данныхДля удобной работы с базой данных необходимо создать database helper, который реализует методы, необходимые нам для работы с БД – такие, как фильтрация по первым буквам и поиск по id. Сам файл RecordsDbHelper.java, как уже говорилось, можно посмотреть по ссылке ниже. Здесь стоит отметить лишь, что мы реализовали методы поиска по id и по текстовому фильтру, которые будем в дальнейшем использовать.
В общем, тут всё как обычно, никаких премудростей.
3. АдаптерЭто класс, реализующий связь списка с Базой данных. В результате запроса к БД мы получаем курсор таблицы. Адаптер берет курсор, список, названия полей таблицы и id элементов из item.xml (см.ниже), которые нужно соотнести друг с другом, и заполняет ListView. Наследуемся от стандартного SimpleCursorAdapter, в котором всё это реализовано, и переопределяем в нём метод getView.
Adapter.java В методе getView готовому View элемента списка(т.е. строке списка с названием станции и цветным кружочком) в тэг(который есть у каждого объекта типа View) помещается id строки таблицы БД, которой этот элемент соответствует. Этот id впоследствии будет передаваться в активити с картой при тапе по элементу списка.
4. СписокСначала нужно задать его Layout – а именно каркас визуального представления необходимых нам элементов:
Layout отдельного элемента списка (картинка – кружочек с цветом линии метро и название станции):
Теперь, наконец, класс Search, в котором описывается активити с поиском. Он реализован с помощью наследования от ListActivity – специального активити для списков. Данные о станциях (название, координаты и номер ветки метро) берутся из вышерассмотренной базы данных.
Самое интересное здесь, как мне кажется, Binder. Он позволяет определить, каким образом привязывать значение из таблицы БД к элементу строки списка, то есть, например, как привязать номер ветки, который хранится в таблице, к соответствующему рисунку с кружочком нужного цвета. Метод setViewValue вызывается для каждого view, указанного в конструкторе курсора. Если возвращаемый результат true, то данные привязаны, всё хорошо. Если false — система понимает, что вызван не подходящий метод и ищет нужный среди стандартных Binder’ов. В этом примере проверяется, является ли view, переданный в качестве параметра, тем самым ImageView, в котором должен быть кружочек. Если это так, то передаем туда соответствующую картинку, иначе возвращаем false, дав понять, что нужно использовать стандартный Binder (т.к. это не картинка, а тут мы обрабатываем только картинки).
При вызове активити первым делом вызывается метод onCreate(), в котором инициализируются все отображаемые элементы. Здесь устанавливается обработчик кликов по элементам списка, который вытягивает тэг с id записи и передаёт этот тэг в активити с картой (ну и собственно вызывает это активити). Также устанавливается обработчик изменения текста в EditText (строка фильтрации). При изменении текста вызывается функция updateList(), в которой с помощью описанного выше хелпера кидается запрос в базу, вытаскивающий отфильтрованные данные, и создается адаптер для списка.
5. КартаНаконец переходим к самим картам. Начнем с Layout:
Здесь требуется указать ключ к GoogleMaps API. Свой я светить не стал :) О том, как его получить, можно почитать тут. Ну и собственно активити с картой:
Здесь: 1. На карту устанавливаются стандартные кнопки ZoomIn и ZoomOut (с помощью setBuiltInZoomControls()) 2. Открывается наша БД. 3. Из intent, который вызвал это активити, вытягивается id записи, который передавался в обработчике клика по элементу списка (я писал об этом выше), и по этому id из базы достается нужная строчка, из которой нам нужны координаты станции. 4. Координаты вытаскиваются с помощью метода getDouble, полученного в результате запроса. 5. Далее создаётся GeoPoint и выполняются преобразования координат (в базе хранятся дробные, а гуглу нужны целочисленные. для этого нужно взятые из базы умножить на 10^6 и округлить). 6. animateTo() и setZoom() сдвигают карту в нужное место и устанавливают уровень зума.
Фух, в общем всё. Как видно, за достаточно простым функционалом стоят не совсем тривиальные вещи, разобраться с которыми новичку не так просто. Поэтому я очень надеюсь, что опыт, которым я поделился, поможет кому-то не потерять энтузиазм из-за проблем на старте, и качественных приложений, оптимизирующих нашу жизнь, будет больше!