Статьи

Использование Android Location API в приложении Погода — Поиск города

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

  • Используя шаблон имени
  • Использование географических координат (Android Location API)

Кроме того, мы познакомимся с некоторыми понятиями об API определения местоположения Android и сервисе определения местоположения .

Поиск города по названию

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

android_weather_app_search_city [4]

В качестве первого шага мы можем использовать EditText, чтобы пользователь мог вставить название города:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
....
    <EditText
        android:id="@+id/cityEdtText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/textView1"
        android:layout_marginTop="5dp"
        android:imeOptions="actionSearch"
        android:inputType="text" >
        <requestFocus />
    </EditText>
....
 
    <ListView android:id="@+id/cityList"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_below="@id/txt1"
              android:layout_marginTop="3dp"/>
 
</RelativeLayout>

Обратите внимание, что в строке 13 мы использовали imeOptions со значением «actionSearch», чтобы указать действие поиска, когда пользователь завершает вставку шаблона названия города. Теперь в Activity, которая содержит этот макет, мы имеем:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
final EditText edt = (EditText) findViewById(R.id.cityEdtText);
 
edt.setOnEditorActionListener(new TextView.OnEditorActionListener() {
 
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_SEARCH) {
            bar.setVisibility(View.VISIBLE);
            JSONSearchTask task = new JSONSearchTask();
            String pattern = edt.getEditableText().toString();
            task.execute(new String[]{pattern});
            return true;
        }
 
        return false;
    }
});

Поэтому, когда пользователь нажимает на значок поиска, мы начинаем поиск подходящих названий городов и в конце заполняем ListView (строка 18). Как вы можете заметить, мы запускаем AsyncTask , потому что операция поиска может потребовать довольно много времени, и мы не хотим проблем с ANR.
Теперь пришло время реализовать логику, которая вызывает удаленный сервис openweathermap и извлекает города. Из API мы знаем, что URL для вызова:

1
http://api.openweathermap.org/data/2.5/find?mode=json&type=like&q=...&cnt=10

где после q мы добавим наше имя шаблона, а cnt представляет количество элементов, которые мы хотим получить (в нашем случае мы хотим максимум 10 результатов).
Как только у нас есть данные, мы их анализируем. Мы ищем тег списка, который содержит результат, и для каждого элемента в результате мы ищем тег имени, который содержит имя , идентификатор и страну. Парсер очень прост:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static List<City> getCityList(String data) throws JSONException {
    JSONObject jObj = new JSONObject(data);
    JSONArray jArr = jObj.getJSONArray("list");
      
    List<City> cityList = new ArrayList<City>();
      
    for (int i=0; i < jArr.length(); i++) {
        JSONObject obj = jArr.getJSONObject(i);
          
        String name = obj.getString("name");
        String id = obj.getString("id");
          
        JSONObject sys = obj.getJSONObject("sys");
        String country = sys.getString("country");
          
        City c = new City(id,name,country);
          
        cityList.add(c);
    }
      
      
    return cityList;
}

… и наконец мы заполняем ListView, чтобы показать результаты пользователю. Это происходит в методе OnPostExecute AsyncTask . Обратите внимание, что вы можете создать собственный адаптер для отображения этой информации или использовать стандартный адаптер.

Поиск города с использованием географических координат: Android Location API

Еще один способ поиска города — использование текущей позиции пользователя. Мы можем использовать услугу определения местоположения, чтобы найти текущее положение устройства с помощью GPS или WI-FI или сотовой сети. Существует два основных элемента при реализации сервиса на основе местоположения:

  • Менеджер местоположений, который является точкой входа в службу на основе местоположения
  • Location Provider, представляющий технологию определения местоположения, используемую для определения положения устройства

Первый шаг — получение ссылки на LocationManager . Это системный сервис, тогда мы можем использовать:

1
LocationManager locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

Как только у нас появится справка по Location Manager, мы сможем найти поставщика. В нашем случае мы не хотим указывать конкретного провайдера (например, GPS), но позволяем системе найти лучшее соответствие критериям поиска, которые мы установили:

1
2
3
4
5
6
7
8
private static Criteria searchProviderCriteria = new Criteria();
 
// Location Criteria
static {
    searchProviderCriteria.setPowerRequirement(Criteria.POWER_LOW);
    searchProviderCriteria.setAccuracy(Criteria.ACCURACY_COARSE);
    searchProviderCriteria.setCostAllowed(false);
}

и тогда мы готовы получить провайдера:

1
String provider = locManager.getBestProvider(searchProviderCriteria, true);

и:

1
Location loc = locManager.getLastKnownLocation(provider);

Теперь мы должны проверить, является ли местоположение нулевым или слишком старым, в этом случае мы получаем новую позицию:

1
2
3
4
5
6
7
8
9
if (loc == null || (SystemClock.elapsedRealtime() - loc.getTime()) > timeOut) {
    // We request another update Location
    Log.d("SwA", "Request location");
    locManager.requestSingleUpdate(provider, locListener, null);
}
else {
    JSONSearchTask task = new JSONSearchTask();
    task.execute(new Location[]{loc});
}

в противном случае мы просто позвоним в службу, чтобы получить город. Обратите внимание на строку 4, что нам нужен слушатель, который реализует некоторые методы обратного вызова, которые будут вызываться, когда доступна текущая позиция:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private LocationListener locListener = new LocationListener() {
 
  @Override
  public void onStatusChanged(String provider, int status, Bundle extras) {
  }
 
  @Override
  public void onProviderEnabled(String provider) {
  }
 
  @Override
  public void onProviderDisabled(String provider) {
  }
 
  @Override
  public void onLocationChanged(Location location) {       
      Log.d("SwA", "Location changed!");
      String sLat =   "" + location.getLatitude();       
      String sLon =  "" + location.getLongitude();
      Log.d("SwA", "Lat ["+sLat+"] - sLong ["+sLon+"]");
 
      LocationManager locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
      locManager.removeUpdates(locListener);
      JSONSearchTask task = new JSONSearchTask();
      task.execute(new Location[]{location});
  }
};

Последний шаг — создание URL-адреса, который будет вызываться для получения города по географическим координатам. В этом случае URL для вызова:

где% lat% и & lon% будут заменены реальным значением, извлеченным из текущей позиции.

Использование эмулятора для тестирования приложения

Последний шаг — тестирование приложения с передачей координат. Мы можем использовать DDMS DDMS и передать координаты:

android_ddms_location [4]

Обратите внимание, что вы должны разделять десятичную часть в соответствии с вашим языком и числовым форматом. Другой способ использования

1
telnet iphost port

и при подключении отправить

1
geo fix <longitude value> <latitude value>