Статьи

Android SDK: создание приложения Mall Finder — достопримечательности

В этом руководстве вы узнаете, как использовать API Карт Google для наложения точек интереса (POI) и использовать доступные службы на основе местоположения на своем телефоне, чтобы показать свое положение относительно доступных местоположений POI. В этом случае мы будем использовать торговые центры в качестве POI.

В первой части этой серии мы рассмотрели, как использовать MapView и получить ваше текущее местоположение с помощью прослушивателя местоположения. В этом уроке мы подробно остановимся на том, что было сделано в первом уроке, и добавим оверлеи, чтобы показать вашу текущую позицию и местоположение некоторых достопримечательностей (POI). Мы сделаем это с помощью внешней библиотеки, которая уже была разработана Джеффом Гифельтом. Если вы не завершили первый урок, я настоятельно советую вам сделать это перед началом этого. Однако, если вы решите не делать этого, вы можете использовать исходные файлы, включенные в конце последнего учебного руководства, в качестве отправной точки для этого.

Вы должны отметить, что в предыдущем уроке мы уже рассмотрели, как узнать ваше текущее местоположение. В этом уроке мы будем жестко кодировать наше местоположение во Флоридском университете (это только ради этого урока, поэтому мы можем показать торговые центры в этой области).

Поскольку мы продолжим с последнего урока, вы должны продолжить и открыть этот проект.


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

Эта информация будет отображаться в виде белого текста на полупрозрачном черном фоне в верхней части MapView.

Откройте файл макета (main.xml), который находится в MallFinder> res> layout> main.xml

Добавьте следующий код после закрывающего тега MapView, но перед закрывающим тегом FrameLayout:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<LinearLayout
    android:orientation=»horizontal»
    android:layout_width=»fill_parent»
    android:layout_height=»wrap_content»
    android:id=»@+id/infoLinearLayout»
    android:clickable=»true»
    android:onClick=»centerToCurrentLocation»>
    <TableLayout
        android:layout_width=»fill_parent»
        android:layout_height=»wrap_content»
        android:layout_marginTop=»30dp»
        android:background=»#97000000″
        android:padding=»7sp»>
        <TableRow>
            <TextView
            android:layout_width=»fill_parent»
            android:layout_height=»wrap_content»
            android:layout_weight=»1″
            android:text=»@string/latitude»
            android:id=»@+id/latitudeText»
            android:textColor=»#FFFFFF»>
            </TextView>
            <TextView
            android:layout_width=»fill_parent»
            android:layout_height=»wrap_content»
            android:layout_weight=»1″
            android:text=»@string/longitude»
            android:id=»@+id/longitudeText»
            android:textColor=»#FFFFFF»>
            </TextView>
        </TableRow>
        <TableRow>
            <TextView
            android:layout_width=»fill_parent»
            android:layout_height=»wrap_content»
            android:layout_weight=»1″
            android:text=»@string/accuracy»
            android:id=»@+id/accuracyText»
            android:textColor=»#FFFFFF»>
            </TextView>
            <TextView
            android:layout_width=»fill_parent»
            android:layout_height=»wrap_content»
            android:layout_weight=»1″
            android:text=»@string/provider»
            android:id=»@+id/providerText»
            android:textColor=»#FFFFFF»>
            </TextView>
        </TableRow>
    </TableLayout>
</LinearLayout>

Возможно, вы заметили, что для LinearLayout был указан метод onClick, этот метод будет использоваться для центрирования MapView вокруг текущего местоположения пользователя при нажатии на полупрозрачный LinearLayout. Это будет полезно, если пользователь решит прокрутить MapView и захочет быстро вернуть свое текущее местоположение.

В приведенном выше файле была указана пара строк (строки, начинающиеся с android: text = «@ string …»), но эти строки еще не были созданы, поэтому давайте сделаем это сейчас.

Откройте файл strings.xml, расположенный в MallFinder> res> values> strings.xml, и добавьте следующие строки кода.

1
2
3
4
<string name=»latitude»>Latitude : </string>
<string name=»longitude»>Longitude : </string>
<string name=»accuracy»>Accuracy : </string>
<string name=»provider»>Provider : </string>

Теперь, когда мы обновили пользовательский интерфейс, давайте обновим код класса MallFinderActivity.java для отображения наших координат местоположения.

Откройте файл MallFinderActivity.java и добавьте следующую строку в метод getLastLocation:

1
((TextView)findViewById(R.id.providerText)).setText(«Provider :» + getBestProvider());

Обновите метод setCurrentLocation, включив в него следующие строки:

1
2
3
((TextView)findViewById(R.id.latitudeText)).setText(«Latitude : » + String.valueOf((int)(location.getLatitude()*1E6)));
((TextView)findViewById(R.id.longitudeText)).setText(«Longitude : » + String.valueOf((int)(location.getLongitude()*1E6)));
((TextView)findViewById(R.id.accuracyText)).setText(«Accuracy : » + String.valueOf(location.getAccuracy()) + » m»);

Наконец, добавьте следующий метод:

1
2
3
public void centerToCurrentLocation(View view){
        animateToCurrentLocation();
}

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

Изображение с текущим местоположением

Мы собираемся начать добавлять наложения в наш MapView, чтобы показать нашу текущую позицию, а также позиции различных достопримечательностей. Для этого мы будем использовать настроенный класс BalloonItemizedOverlay, который был разработан Джеффом Джифелтом и расположен по адресу https://github.com/jgilfelt/android-mapviewballoons . Следует отметить, что в Android есть свой собственный класс ItemizedOverlay, который можно использовать для наложения графики на MapView. Я выбрал этот класс над классом ItemizedOverlay, поскольку он предоставляет всплывающую подсказку при нажатии наложенного изображения, функцию, которую делает класс ItemizedOverlay. не по умолчанию. Это также даст нам возможность увидеть, как настраивать и включать внешние библиотечные проекты в наши собственные проекты.

Начнем с загрузки файла. Откройте веб-браузер и перейдите по адресу https://github.com/jgilfelt/android-mapviewballoons/downloads и нажмите кнопку «Загрузить в формате zip».

После загрузки разархивируйте файл и запомните его местоположение.

В Eclipse перейдите к «Файл»> «Импорт». В новом окне выберите «Существующие проекты в рабочую область» и нажмите «Далее».

Импорт внешней библиотеки

В поле «Выберите корневой каталог» перейдите к папке, в которую вы распаковали BalloonItemizedOverlay, и выберите ее. В области Projects требуется только проект android-mapviewsballoon. Однако вы можете выбрать оба проекта, если хотите включить пример проекта.
Убедитесь, что установлен флажок Копировать проекты в рабочую область. Нажмите готово.

Импорт внешней библиотеки Шаг 2

Теперь вы должны увидеть проект android-mapviewballoons в окне проводника пакетов.

Щелкните правой кнопкой мыши по проекту android-mapviewballoons и выберите свойства. Появится новое окно. В левом столбце под текстовым полем «Тип фильтра текста» нажмите на Android в правой части окна и убедитесь, что установлен флажок «Есть библиотека», если его нет, выберите его и нажмите «Применить», а затем «ОК».

Импорт внешней библиотеки Шаг 3

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

У нас будет проект MallFinder, используйте библиотеку, которую мы включили выше. Для этого щелкните правой кнопкой мыши по проекту MallFinder в окне Package Explorer в Eclipse, затем выберите Properties и выберите Android в левом столбце, как вы это делали в предыдущем шаге. В правом столбце найдите область «Библиотека» (ту же область на предыдущем шаге, где вы выбрали «Библиотека»), но на этот раз нажмите кнопку «Добавить». Появится новое окно выбора проекта. Выберите android-mapviewballoons и затем нажмите OK.

Импорт внешней библиотеки Шаг 4

Вы должны увидеть запись со ссылкой на проект. Выберите android-mapballoons и нажмите Apply. Далее нажмите ОК.


Теперь мы собираемся создать класс MallOverlay, который расширяет класс BalloonItemizedOverlay. Перейдите в MallFinder> src> com.shawnbe.mallfinder. Щелкните правой кнопкой мыши на com.shawnbe.mallfinder в окне Package Explorer. Затем выберите New> Class. Назовите класс MallOverlay и нажмите «Готово».

Создание класса наложения

Нам нужно обновить класс MallOverlay, чтобы расширить BalloonItemizedOverlay.

Сделайте это, обновив строку:

1
public class MallOverlay {

чтобы:

1
public class MallOverlay extends BalloonItemizedOverlay<OverlayItem> {

Нам также нужно импортировать несколько предметов. Для этого добавьте следующие строки вверху класса MallOverlay.java:

1
2
3
4
import android.graphics.drawable.Drawable;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.readystatesoftware.mapviewballoons.BalloonItemizedOverlay;

Далее нам нужно добавить конструктор и реализовать некоторые необходимые методы при расширении класса BalloonItemizedOverlay:

Примечание: Eclipse позволяет нам легко исправлять ошибки и добавлять нереализованные методы, где это применимо, уведомляя нас об ошибках и предлагая исправления, где это возможно.

Например, прежде чем мы добавим конструктор или необходимые методы, вы заметите, что слово MallOverlay подчеркнуто красным в строке

1
public class MallOverlay extends BalloonItemizedOverlay<OverlayItem> {

Это указывает на то, что произошла ошибка. Наведите курсор на слово MallOverlay, и появится информационное диалоговое окно, сообщающее, что вы должны определить конструктор, и есть одно быстрое исправление.

Ошибка автофикса

Нажмите на ссылку Добавить конструктор MallOverlay (Drawable, MapView), и вам будет добавлен конструктор скелета.

Обратите внимание, что слово MallOverlay все еще подчеркнуто. Подведите курсор мыши еще раз, и вы увидите, что это потому, что существуют нереализованные методы. Нажмите на ссылку «добавить невыполненные методы», и два скелетных метода будут созданы для вас.

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

Класс MallOverlay должен выглядеть следующим образом

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
28
package com.shawnbe.mallfinder;
 
import android.graphics.drawable.Drawable;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.readystatesoftware.mapviewballoons.BalloonItemizedOverlay;
 
 
public class MallOverlay extends BalloonItemizedOverlay<OverlayItem> {
 
    public MallOverlay(Drawable defaultMarker, MapView mapView) {
        super(defaultMarker, mapView);
        // TODO Auto-generated constructor stub
    }
 
    @Override
    protected OverlayItem createItem(int i) {
        // TODO Auto-generated method stub
        return null;
    }
 
    @Override
    public int size() {
        // TODO Auto-generated method stub
        return 0;
    }
 
}

Теперь мы собираемся добавить некоторый функциональный код в методы скелета. Объявите эти три глобальные переменные:

1
2
3
private Context mContext;
private ArrayList<OverlayItem> malls = new ArrayList<OverlayItem>();
private Location currentLocation;

Обновите и добавьте методы в классе MallOverlay, чтобы они были такими же, как показано ниже:

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
28
29
public MallOverlay(Drawable defaultMarker, MapView mapView) {
    super(boundCenter(defaultMarker),mapView);
    boundCenter(defaultMarker);
    mContext = mapView.getContext();
}
 
@Override
protected OverlayItem createItem(int i) {
    // TODO Auto-generated method stub
    return malls.get(i);
}
 
@Override
    public int size() {
    // TODO Auto-generated method stub
    return malls.size();
}
     
public void addOverlay(OverlayItem overlay) {
    malls.add(overlay);
    populate();
}
     
@Override
protected boolean onBalloonTap(int index, OverlayItem item) {
    Toast.makeText(mContext, «Overlay Item » + index + » tapped!»,
            Toast.LENGTH_LONG).show();
    return true;
}

Теперь у нас есть класс, который по большей части обрабатывает отрисовку наложений в MapView.


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

Иконка Мое местоположение
Иконка Торговый центр

Загрузите эти две графики и поместите их в папку для рисования. Поскольку я имею дело с экраном HDPI, я буду использовать папку Mallfinder> res> drawable-hdpi. Следует помнить, что при разработке приложения для публичного использования вы не знаете, какую плотность пикселей будет иметь трубка пользователя, поэтому вам следует создать соответствующую графику для каждой из трех плотностей. Это предотвратит растягивание и сжатие вашей графики, так как это может заставить вашу графику выглядеть не так, как вы предполагали.

Мы создадим метод, который займет нашу текущую позицию и наложит нарисуемое изображение.

Добавьте метод ниже к классу MallFinderActivity:

01
02
03
04
05
06
07
08
09
10
11
12
public void drawCurrPositionOverlay(){
    List<Overlay> overlays = mapView.getOverlays();
    overlays.remove(currPos);
    Drawable marker = getResources().getDrawable(R.drawable.me);
    currPos = new MallOverlay(marker,mapView);
    if(currentPoint!=null){
        OverlayItem overlayitem = new OverlayItem(currentPoint, «Me», «Here I am!»);
        currPos.addOverlay(overlayitem);
        overlays.add(currPos);
        currPos.setCurrentLocation(currentLocation);
    }
}

Кроме того, добавьте следующую строку в метод onCreate после вызова метода getLastLocation, а также в конец метода setCurrentLocation.

1
drawCurrPositionOverlay();

Запустите приложение, и вы увидите синий значок, показывающий ваше текущее местоположение.

MapView Отображение текущего местоположения

До сих пор в нашем уроке мы показали, как получить ваше текущее местоположение и пометить его с помощью элемента наложения. Однако, ради остальной части учебника, мы будем жестко указывать ваше текущее местоположение во Флоридском международном университете (ПФР). В нашем примере я буду использовать торговые центры в районе ПФР. Вместо того чтобы каждый раз прокручивать эту область, вы запускаете приложение, которое мы разместим в ПФР.

Давайте сделаем это сейчас, открыв класс MallFinderActivity и прокомментировав первые три строки кода в методе setCurrentLocation и добавив эти четыре строки кода после закомментированной области.

1
2
3
4
currentPoint = new GeoPoint(29647929,-82352486);
currentLocation = new Location(«»);
currentLocation.setLatitude(currentPoint.getLatitudeE6() / 1e6);
currentLocation.setLongitude(currentPoint.getLongitudeE6() / 1e6);

Четыре строки кода выше также должны быть помещены перед строками в методе getLastLocation:

1
2
3
if(currentLocation != null){
        setCurrentLocation(currentLocation);
}

В настоящее время метод setCurrentLocation должен быть:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public void setCurrentLocation(Location location){
     /* int currLatitude = (int) (location.getLatitude()*1E6);
        int currLongitude = (int) (location.getLongitude()*1E6);
        currentPoint = new GeoPoint(currLatitude,currLongitude);
         
        /*========================================================================================
        /*The Above Code displays your correct current location, but for the sake of the demo
        I will be hard coding your current location to the University of Florida, to get your real
        current location, comment or delete the line of code below and uncomment the code above.
         
        currentPoint = new GeoPoint(29647929,-82352486);
        currentLocation = new Location(«»);
        currentLocation.setLatitude(currentPoint.getLatitudeE6() / 1e6);
        currentLocation.setLongitude(currentPoint.getLongitudeE6() / 1e6);
         
         
        ((TextView)findViewById(R.id.latitudeText)).setText(«Latitude : » + String.valueOf((int)(currentLocation.getLatitude()*1E6)));
        ((TextView)findViewById(R.id.longitudeText)).setText(«Longitude : » + String.valueOf((int)(currentLocation.getLongitude()*1E6)));
        ((TextView)findViewById(R.id.accuracyText)).setText(«Accuracy : » + String.valueOf(location.getAccuracy()) + » m»);
        drawCurrPositionOverlay();
    }

и метод getLastLocation:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
public void getLastLocation(){
    String provider = getBestProvider();
    currentLocation = locationManager.getLastKnownLocation(provider);
         
    /*The next 4 lines are used to hardcode our location
     * If you wish to get your current location remember to
     * comment or remove them */
         
    currentPoint = new GeoPoint(29647929,-82352486);
    currentLocation = new Location(«»);
    currentLocation.setLatitude(currentPoint.getLatitudeE6() / 1e6);
    currentLocation.setLongitude(currentPoint.getLongitudeE6() / 1e6);
         
    if(currentLocation != null){
        setCurrentLocation(currentLocation);
    }
    else
    {
        Toast.makeText(this, «Location not yet acquired», Toast.LENGTH_LONG).show();
    }
    ((TextView)findViewById(R.id.providerText)).setText(«Provider :» + getBestProvider());
}

Добавьте следующий метод к классу MallFinderActivity, он жестко закодирует некоторые торговые зоны, расположенные рядом с ПФР.

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
28
29
30
31
public void drawMalls(){
    Drawable marker = getResources().getDrawable(R.drawable.malls);
    MallOverlay mallsPos = new MallOverlay(marker,mapView);
        GeoPoint[] mallCoords = new GeoPoint[6];
         
        //Load Some Random Coordinates in Miami, FL
        mallCoords[0] = new GeoPoint(29656582,-82411151);//The Oaks Mall
        mallCoords[1] = new GeoPoint(29649831,-82376347);//Creekside mall
        mallCoords[2] = new GeoPoint(29674146,-8238905);//Millhopper Shopping Center
        mallCoords[3] = new GeoPoint(29675078,-82322617);//Northside Shopping Center
        mallCoords[4] = new GeoPoint(29677017,-82339761);//Gainesville Mall
        mallCoords[5] = new GeoPoint(29663835,-82325599);//Gainesville Shopping Center
     
         
        List<Overlay> overlays = mapView.getOverlays();
    OverlayItem overlayItem = new OverlayItem(mallCoords[0], «The Oaks Mall», «6419 W Newberry Rd, Gainesville, FL 32605»);
    mallsPos.addOverlay(overlayItem);
    overlayItem = new OverlayItem(mallCoords[1], «Creekside Mall», «3501 Southwest 2nd Avenue, Gainesville, FL»);
    mallsPos.addOverlay(overlayItem);
    overlayItem = new OverlayItem(mallCoords[2], «Millhopper Shopping Center», «NW 43rd St & NW 16th Blvd. Gainesville, FL»);
    mallsPos.addOverlay(overlayItem);
    overlayItem = new OverlayItem(mallCoords[3], «Northside Shopping Center», «Gainesville, FL»);
    mallsPos.addOverlay(overlayItem);
    overlayItem = new OverlayItem(mallCoords[4], «Gainesville Mall», «2624 Northwest 13th Street Gainesville, FL 32609-2834»);
    mallsPos.addOverlay(overlayItem);
    overlayItem = new OverlayItem(mallCoords[5], «Gainesville Shopping Center», «1344 N Main St Gainesville, Florida 32601»);
    mallsPos.addOverlay(overlayItem);
    overlays.add(mallsPos);
         
    mallsPos.setCurrentLocation(currentLocation);
}

Нам нужно вызвать вышеуказанный метод при загрузке страницы. Для этого добавьте следующую строку после метода drawCurrPositionOverlay () в методе onCreate:

1
drawMalls();

Запустите приложение, и ваше текущее местоположение должно быть ПФР, и вы также должны увидеть 5 красных маркеров, представляющих торговые центры вокруг этой области:

Mapview с указанием расположения торговых центров

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

Откройте файл MallOverlay.java, расположенный в MallFinder> com.shawnbe.mallfinder> MallOverlay.java, и добавьте метод ниже. Этот метод принимает GeoPoint в качестве аргумента и возвращает местоположение. Чтобы использовать метод distanceTo, предоставляемый Android, нам нужно предоставить две позиции в качестве объектов местоположения, наш метод ниже преобразует GeoPoint в объект местоположения:

1
2
3
4
5
6
public Location convertGpToLoc(GeoPoint gp){
    Location convertedLocation = new Location(«»);
    convertedLocation.setLatitude(gp.getLatitudeE6() / 1e6);
    convertedLocation.setLongitude(gp.getLongitudeE6() / 1e6);
    return convertedLocation;
}

Теперь нам нужно обновить метод onBalloonTap, чтобы вычислить расстояние между нашим местоположением и выбранным оверлеем и отобразить информацию в виде тоста.

Обновите метод onBalloonTap следующим образом:

01
02
03
04
05
06
07
08
09
10
11
@Override
protected boolean onBalloonTap(int index, OverlayItem item) {
    String tmp = malls.get(index).getTitle();
    GeoPoint mallPoint = malls.get(index).getPoint();
    Location tmpLoc = convertGpToLoc(mallPoint);
    double distance = ((currentLocation).distanceTo(tmpLoc))*(0.000621371192);
    DecimalFormat df = new DecimalFormat(«#.##»);
    tmp = tmp + » is » + String.valueOf(df.format(distance)) + » miles away.»;
    Toast.makeText(mContext,tmp,Toast.LENGTH_LONG).show();
    return true;
}

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

MapView отображает информацию о местоположении

Там это рабочий MapView с оверлеями. За последние два урока вы узнали, как использовать объект MapView, зарегистрировать ключ API, получить ваше текущее местоположение, указать критерии для вашего предпочтительного поставщика, использовать внешние библиотеки, анимировать позицию в MapView, добавить наложения на различные точки. на карте, и даже добавить всплывающие окна с воздушными шарами. Я надеюсь, что это руководство было информативным и дает вам знания, необходимые для создания собственного приложения, основанного на местоположении Не стесняйтесь, напишите мне на [email protected], если что-то не было объяснено должным образом, и я сделаю все возможное, чтобы уточнить, или если у вас есть предложения или общие вопросы или комментарии, не стесняйтесь поделиться!