- PVSM.RU - https://www.pvsm.ru -
Пользователям не хочется разбираться с особенностями координат, часовых поясов. Некоторые даже не знают, как эти координаты выражаются, и что такое часовые пояса.
Как сделать, чтобы было хорошо пользователю?
В данной статье будет разобрано, как работать с координатами и часовыми поясами:
А именно,
Итак,
Пользователь хочет:
Поэтому мы просто обязаны научить телефон:
Для торопливых: нажмите ссылку [1], чтобы перейти сразу к приложению и описанию кода с комментариями.
Для остальных. Давайте по-порядку.
Кстати, бывает, что класс Calendar
в Java
выдаёт ошибку со сдвигом на час, если инициализировать, например, с текущем временем и часовым поясом, а затем установить время до того, как мы перешли исключительно на летнее время, например, 1988г., то календарь будет выводить часы со сдвигом на 1 час. Лечится это, если постоянно при установке новых миллисекунд заново инициализировать календарь. SimpleDateFormat
всегда выдаёт подобную ошибку, потому не советую им пользоваться для старых дат.
Ссылка на статью «часовой пояс» в Википедии [2]
Geonames.org [3] — это великолепный ресурс. Там можно как скачать базы данных, так и использовать для этого ресурса родной API. В данной статье я буду использовать как раз API.
Для работы с Geonames нужно регистрировать пользователя, при помощи которого будет доступ к API. А также не забыть его активировать. Для этого зайдите в аккаунт [4] и нажмите там на ссылку: Click here to enable.
Тут документация [5]
Есть даже библиотека для доступа [6], но библиотеку лично я не использую, потому что иногда возникают ошибки. Напрямую с API через http-запрос работать понятнее.
Лицензия [7]. Можно бесплатно пользоваться этим продуктом.
О Google Maps все знают.
Можно общую справку по использованию Google Maps можно посмотреть тут [8].
Также есть такая фишка, как GeoCoder. Можно узнавать адрес по координатам. Но часовых поясов, почему-то там нет.
Для поиска часовых поясов по клику я также буду использовать Geonames.org [3].
Для новичков кратко алгоритм редактирования AndroidManifest.xml
, что откуда брать, чтобы работали карты Google Maps и определение положения телефона.
Кому это не интересно — кликайте сюда, чтобы пролистать сразу к описанию особенностей приложения [1].
1.
внутри
<manifest>
:
<permission
android:name="com.example.android.permission.MAPS_RECEIVE"
android:protectionLevel="signature" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<!-- The following two permissions are not required to use
Google Maps Android API v2, but are recommended. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>
2.
внутри
<application>
:
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="API_KEY"/>
Чтобы получить android:value=«API_KEY» (ссылка, где это в коде [9]), нужно:
APIs & auth > Credentials > Create new KEY > Android key
Картинки для android:value=«API_KEY»
особенности получения SHA-1-KEY
Для работы Google Maps добавить в проект Android-библиотеку. путь такой: <Android-SDK-Path>extrasgooglegoogle_play_services
. Добавлять нужно так: File>New>Project>Android>Android Prject from Existing Code>Next
Не забудьте отметить в настройках основного проекта habrtimezone
добавить проект google_play_services
как библиотеку. Это надо сделать в разделе Project>Properties>Android>Library>Add...
Собирать проект нужно для Google APIs 4.4.2 (можно > 4.0). Для этого поставьте галочку напротив соответствующего пункта списка в Project>Properties>Android>Project Build Target
. Если там такого пункта нет, то необходимо через Android SDK Manager установить Google APIs.
Ниже приведён пример приложения, которое выложено на Google Play [12], исходные коды есть на GitHub [13].
Будут приведены комментарии к особенностям работы приложения.
Итак, что умеет приложение:
1. Определять текущие координаты
2. Работать с картой
3. Определять положение города и его часовой пояс по его названию.
Не буду объяснять все аспекты работы приложения, а лишь покажу основные детали и особенности. При наличии кода [13] с мелочами разобраться не составит труда.
Начинается работа приложения с AMain
Activity
Класс AMain.java
. Код класса [14].
public class AMain extends Activity implements OnClickListener
(ссылка [15])
OnClickListener — для обработки кликов. На мой взгляд, обрабатывать клики лучше таким образом, так как память будет потребляться в меньших количествах, если использовать
button.setOnClickListener(this);
, а не
button.setOnClickListener(<новый Listener>);
Этот эффект особенно заметен, когда используется большое количество кликабельных объектов.
(ссылка для кнопки [16] и ссылка для обработчика [16])
Что тут происходит:
Происходит поиск координат телефона в пространстве при помощи вышек GSM, а также GPS.
Через это окно можно открыть:
1. Карту. Для маркеров на этой карте будут переданы текущее положение телефона и положение (широта/долгота), которое записано в текстовых полях AGMap
2. Поиск городов через интернет. ACityListOnline
Поиск координат сделан при помощи следующих основных объектов:
private LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
private LocationListener gpsLocationListener;
private LocationListener networkLocationListener;
(ссылка [17])
Как только изменятся координаты и/или время, событие будет обработано при помощи LocListener implements LocationListener
(ссылка [18]) в
@Override
public void onLocationChanged(Location location) {}
(ссылка [19])
Работа с картой
Для того, чтобы карта была в приложении, нужно в ресурсах в файле agmap.xml
(ссылка [20]) создать следующий блок:
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"/>
, который и является картой.(ссылка [21])
Класс Activity: AGMap.java
Код класса [22].
Что тут происходит.
1. Открывается карта,
2. В неё передаются координаты с предыдущего экрана.
3. Есть 2 маркера — текущие координаты, и маркер по центру экрана.
4. Можно двигать карту, при нажатии на кнопку Сохранить будут применены координаты центра карты, будет определено название региона, будет определён часовой пояс при помощи Geonames.org.
Особенности настройки и использования карты.
public class AGMap extends FragmentActivity implements OnCameraChangeListener, OnClickListener
(ссылка [23])
FragmentActivity
— так как включена карта в это Activity, от родительским нужно использовать именно этот класс.
OnCameraChangeListener
— нужен для обработки передвижения карты
private GoogleMap mMap; //основной объект (карта),
который инициализирован следующим образом:
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
(ссылка [24])
Положения объектов (указателей на карте) хранятся в
LatLng HEREIAM = null; // текущее положение
LatLng pos = null; // текущее положение центра карты
(ссылка [25])
Для того чтобы нарисовать маркер, нужно сделать объект с настройками маркера
mo = new markerOptions(... //настроить его текст, цвет, и т.п.
(ссылка [26])
и применить его к карте:
mar = mMap.addMarker(mo);
на выходе получится Marker mar
(ссылка [27])
Заметил, что на некоторых устройствах при инициализации карты возникает ошибка в onCreate()
, поэтому приходится ошибку оборачивать в
try{}catch(NullPointerException e){}
(ссылка [28])
Все настройки карты находятся
GoogleMapOptions options = new GoogleMapOptions();// настройки карты
(ссылка [29])
Центрирование маркера по карте:
@Override
public void onCameraChange(CameraPosition cp) {
// TODO Auto-generated method stub
pos = cp.target;
mar1.setPosition(pos);
}
(ссылка [30])
Изначально в эту функцию были добавлены функции обработки результата
uniqueExec(); //поиск названия положения
uniqueExecTZ(pos); //поиск часового пояса
Но в случае плохого соединения будет всё притормаживать. Использовать это неудобно.
Поиск название положения происходит таким образом:
Geocoder geoCoder = new Geocoder(getBaseContext(), Locale.getDefault());
try {
List<Address> addresses = geoCoder.getFromLocation(point.latitude, point.longitude, 1);// тут и будет список адресов
}
catch (IOException e) {
e.printStackTrace();
}
return address;
}
(ссылка [31])
Справедливости ради стоит добавить, что иногда возникает ошибка «Service not Available». В этом случае лучше дополнить эту функцию проверкой вида
if (Geocoder.isPresent()){}
else{}
и в else
сделать прямой запрос по URL
:
String googleMapUrl = "http://maps.googleapis.com/maps/api/geocode/json?latlng=" + point.latitude + ","
+ point.longitude + "&sensor=false&language=" + Locale.getDefault().getLanguage();
В данном случае добавлять условие if-else
надо будет тут [32].
На выходе будет JSON-объект, который также разбирается на кусочки как в случае с GeoNames.(см. чуть ниже [33])
К сожалению, есть ограничение на 2500 запросов в день в случае бесплатного варианта.
Подробней тут [34]
При нажатии на кнопку сохранить (ссылка [32]) происходит следующее:
1. поиск названия местности по координатам в центре экрана. Выполнено при помощи geocodertask extends AsyncTask<Void, Integer, Void>
(ссылка [35]) в функции uniqueExec();
(ссылка [36])
2. поиск часового пояса по координатам в центре экрана. Выполнено при помощи TimeZoneTask extends AsyncTask<Double, Integer, Void>
(ссылка [37]) в функции uniqueExecTZ(pos);
(ссылка [38])
Запросы по http для Geocoder
и Time Zone
обвёрнуты в AsyncTask
(ссылка 1 [32] и ссылка 2 [39]), потому что доступ в интернет должен происходить асинхронно, не забивая основной поток.
По сути, главная часть приложения и данной статьи — это несколько запросов на Geonames.org. Вот первый из них.
String[] urlString = {"http://api.geonames.org/findNearbyJSON?lat=","&lng=", "&radius=50&username=","&style=full&maxRows=1"};
outURL = urlString[0] + f.format(lat).replace(",", ".") + urlString[1] + f.format(lon).replace(",", ".") + urlString[2] + _.names1[r.nextInt(_.names1.length)] + urlString[3];
(ссылка 1 [40])
(ссылка 2 [41])
В запросе происходит поиск любого одного объекта на расстоянии ближе 50 км., если этот объект имеет часовой пояс, то это является результатом. Если нет или такие объекты не обнаружены (например, расположение где-то в океане), то производятся вычисление часового пояса по текущей широте и долготе с обычным делением глобуса на 24 части (ссылка [42]). Возможно объект выбирается не произвольным образом, а берётся ближайший, но я это детально не проверял.
Результат выводится в формате JSON. Поэтому параллельно можно вытянуть и другую информацию.
Массив names1
(ссылка [43]) из класса _.java
хранит в себе логины на Geonames.org. Можно хранить один логин, но лучше несколько, так как есть ограничение на количество запросов в час. В этом случае, увеличив количество пользователей и выбирая их случайным образом, можно будет таким искусственным способом пропорционально увеличить и количество запросов. Выбор пользователя, через которого происходит соединение, возникает случайным образом при помощи r.nextInt() (что такое r [44]). В лицензии я не нашёл ограничений для подобной уловке.
Ещё раз напишу. Внимание, не забудьте активировать пользователя. Для этого зайдите в аккаунт [4] и нажмите там на ссылку: Click here to enable.
Метод, конечно, не абсолютно точный, то зато рабочий. И обычно такого метода вполне достаточно. От конечных пользователей претензий пока не поступало.
Activity ACityListOnline.java
Код класса [45].
Также используется Geonames.org в
CityTask extends AsyncTask<String, Integer, Void>
(ссылка на класс [46])
(тут выполняется запуск объекта класса CityTask [47])
Здесь запросы аналогичны AGMap.java
, но происходит поиск по названию.
Вот второй из тех запросов, которые работают с GeoNames:
String[] urlString = {"http://api.geonames.org/searchJSON?q=", "&username=","&style=full"};
outURL = urlString[0] + URLEncoder.encode(str, "UTF-8") + urlString[1] + _.names1[r.nextInt(_.names1.length)] + urlString[2];
(ссылка 1 [48])
(ссылка 2 [49])
Если результат найден, то сохраняется в объект класса _Info
(ссылка на класс [50])
(ссылка на список объектов класса, которые хранят результаты поиска [51])
Далее детальную информацию можно просмотреть в другом Activity, кликнув на элемент ListView
.
Сейчас в объекте класса _Info
хранятся широта, долгота, часовой пояс и международное название региона. Если хотите вытащить другую информацию (она также доступна), можете её взять из JSON-объекта самостоятельно. Всё просто и нудно.
По основному функционалу всё.
Ещё раз ссылка на Google Play [12].
Перед тем как использовать код на GitHub [13] обратите ВНИМАНИЕ:
В этом коде нет пользователей от Geonames.org
А именно, необходимо будет вставить имена пользователей в переменную
public static final String[] names1 = {"your_account_1","your_account_2"};
в классе _.java
(ссылка [43]), а также вставить свой API_KEY
. в AndroidManifest.xml
(ссылка [9])
Как получить эти значения, я объяснял выше.
Чтобы проект с GitHub собрался, нужно добавить в Project>Properties>Java Build Path>Libraries>Add External JARs
вот этот файл: Android_SDK_Pathextrasandroidsupportv4android-support-v4.jar
. Затем перейти в Project>Properties>Java Build Path>Order and Export
и включить этот файл.
Если есть интерес к подобной статье, могу в следующий раз могу рассказать, как это может быть сделано локально без подключения к интернету.
Автор: Yurevich1
Источник [52]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/52817
Ссылки в тексте:
[1] нажмите ссылку: #Next0
[2] Ссылка на статью «часовой пояс» в Википедии : http://ru.wikipedia.org/w/index.php?title=%D0%A7%D0%B0%D1%81%D0%BE%D0%B2%D0%BE%D0%B9_%D0%BF%D0%BE%D1%8F%D1%81&oldid=60197098
[3] Geonames.org: http://www.geonames.org/
[4] зайдите в аккаунт: http://www.geonames.org/manageaccount
[5] Тут документация: http://www.geonames.org/export/web-services.html
[6] Есть даже библиотека для доступа: http://www.geonames.org/export/client-libraries.html
[7] Лицензия: http://www.geonames.org/about.html
[8] можно посмотреть тут: https://developers.google.com/maps/documentation/android/start
[9] ссылка, где это в коде: https://github.com/yurevich1/habrtimezone/blob/master/AndroidManifest.xml#L52
[10] сюда: https://code.google.com/apis/console/b/0/
[11] developer.android.com/tools/publishing/app-signing.html#cert: http://developer.android.com/tools/publishing/app-signing.html#cert
[12] Google Play: https://play.google.com/store/apps/details?id=ru.ps.habrtimezone
[13] GitHub: https://github.com/yurevich1/habrtimezone
[14] Код класса: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AMain.java
[15] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AMain.java#L26
[16] ссылка для кнопки: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AMain.java#L59
[17] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AMain.java#L67
[18] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AMain.java#L85
[19] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AMain.java#L88
[20] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/res/layout/agmap.xml
[21] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/res/layout/agmap.xml#L34
[22] Код класса: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java
[23] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L53
[24] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L192
[25] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L94
[26] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L114
[27] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L117
[28] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L126
[29] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L105
[30] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L135
[31] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L165
[32] надо будет тут: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L258
[33] см. чуть ниже: #JSONExpl
[34] Подробней тут: https://developers.google.com/maps/documentation/geocoding/?hl=ru
[35] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L242
[36] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L213
[37] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L281
[38] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L214
[39] ссылка 2: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L311
[40] ссылка 1: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L291
[41] ссылка 2: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L334
[42] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L407
[43] ссылка: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/_.java#L41
[44] что такое r: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/AGMap.java#L75
[45] Код класса: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/ACityListOnline.java
[46] ссылка на класс: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/ACityListOnline.java#L183
[47] тут выполняется запуск объекта класса CityTask: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/ACityListOnline.java#L127
[48] ссылка 1: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/ACityListOnline.java#L192
[49] ссылка 2: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/ACityListOnline.java#L233
[50] ссылка на класс: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/libs/info/_Info.java
[51] ссылка на список объектов класса, которые хранят результаты поиска: https://github.com/yurevich1/habrtimezone/blob/master/src/ru/ps/habrtimezone/ACityListOnline.java#L63
[52] Источник: http://habrahabr.ru/post/209050/
Нажмите здесь для печати.