Вступление
Одна из самых полезных функций для пользователей — интеграция карт. В предыдущей части этой серии мы обсуждали, как настроить Google Maps для Android с помощью консоли разработчика Google и как создать основной фрагмент Google Maps. Затем мы добавили различные виды маркеров и способы рисования на карте.
В этом учебном пособии вы расширите то, что узнали в предыдущей статье, чтобы расположить представления поверх карты, переопределить элементы управления селектором внутреннего уровня и добавить компонент «Просмотр улиц» в свои приложения. Исходный код этой статьи можно найти на GitHub.
1. Начало настройки
Для начала выполните шаги, перечисленные в предыдущей статье этой серии , чтобы создать базовый проект с использованием MapFragment , присоединить его к Activity и активировать API Карт Google через Консоль разработчика Google . Для этого урока вам не нужно использовать местоположения классов Play Services, но вам нужно импортировать карты библиотеки Play Services в ваш узел dependencies build.gradle .
|
1
2
3
4
5
|
dependencies {
compile fileTree(dir: ‘libs’, include: [‘*.jar’])
compile ‘com.android.support:appcompat-v7:23.0.0’
compile ‘com.google.android.gms:play-services-maps:7.8.0’
}
|
Как только это будет сделано, вы получите экран, который выглядит следующим образом:

Далее вам нужно настроить камеру. В этом уроке мы сосредоточимся на Мэдисон Сквер Гарден в Нью-Йорке, потому что это отличный пример здания, использующего карты уровней внутри помещений.
В onViewCreated вы можете добавить вызов следующего вспомогательного метода initCamera . Возможно, вы помните, что нам нужно подождать, пока onViewCreated будет работать с Google Maps, потому что именно тогда мы знаем, что объект карты готов к использованию.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
private void initCamera() {
CameraPosition position = CameraPosition.builder()
.target( new LatLng( 40.7506, -73.9936 ) )
.zoom( 18f )
.bearing( 0.0f )
.tilt( 0.0f )
.build();
getMap().animateCamera(
CameraUpdateFactory.newCameraPosition( position ), null );
getMap().setMapType( GoogleMap.MAP_TYPE_HYBRID );
}
|
Вышеупомянутый метод перемещает камеру к нашей цели и увеличивает ее достаточно близко, чтобы селектор в помещении стал видимым. Вы заметите, что на правой стороне экрана есть полоса чисел и наложение на карте для каждого этажа. Когда вы выбираете другой уровень справа, текущий план этажа анимируется на новый. Это функция, с которой вы будете работать позже, чтобы иметь собственный выбор уровня управления просмотром.

Далее вам необходимо реализовать три интерфейса, которые будут использоваться в этом руководстве.
-
GoogleMap.OnIndoorStateChangeListenerиспользуется для определения того, когда селектор уровня в помещении изменил видимость. -
SeekBar.OnSeekBarChangeListenerиспользуется с одним из наших оверлеев вида для управления выбором уровня, а не с использованием набора кнопок по умолчанию справа. -
GoogleMap.OnMapLongClickListenerиспользуется в этом примере для изменения отображаемого местоположения вашего компонента Просмотр улиц.
|
1
2
3
4
|
public class MapFragment extends SupportMapFragment implements
GoogleMap.OnIndoorStateChangeListener,
GoogleMap.OnMapLongClickListener,
SeekBar.OnSeekBarChangeListener {
|
После того, как вы добавили необходимые методы для этих трех интерфейсов, вы можете начать добавлять виды в верхней части карты.
2. Наложение просмотров
Несмотря на то, что базовые функции Карт Google соответствуют большинству потребностей, в некоторые моменты вы захотите добавить дополнительные виды на карту для выполнения действий. Для этого урока мы добавим SeekBar и некоторые объекты TextView , чтобы настроить элементы управления для селектора уровня внутри помещения.
Начните с создания нового XML-файла макета view_map_overlay.xml . Добавьте следующий код, чтобы создать базовый макет, который будет использоваться на экране.
|
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
|
<?xml version=»1.0″ encoding=»utf-8″?>
<RelativeLayout xmlns:android=»http://schemas.android.com/apk/res/android»
android:layout_width=»match_parent»
android:layout_height=»match_parent»>
<LinearLayout
android:layout_width=»match_parent»
android:layout_height=»wrap_content»>
<TextView
android:id=»@+id/indoor_min_level»
android:text=»0″
android:layout_width=»0dp»
android:layout_height=»wrap_content»
android:layout_weight=»1″
android:padding=»4dp»
android:textSize=»20sp»
android:gravity=»center»
android:textColor=»@android:color/white» />
<SeekBar
android:id=»@+id/indoor_level_selector»
android:layout_width=»0dp»
android:layout_height=»wrap_content»
android:layout_weight=»8″ />
<TextView
android:id=»@+id/indoor_max_level»
android:text=»10″
android:layout_width=»0dp»
android:layout_height=»wrap_content»
android:layout_weight=»1″
android:padding=»4dp»
android:textSize=»20sp»
android:textColor=»@android:color/white»
android:gravity=»center» />
</LinearLayout>
</RelativeLayout>
|
Как только ваш файл макета завершен, вы можете добавить его в качестве наложения на фрагмент карты. В onCreateView вам необходимо получить доступ к родительскому ViewGroup , накачать новое наложение макета и прикрепить его к родительскому ViewGroup . Здесь также сохраняются ссылки на каждое из представлений в оверлее, чтобы они могли быть изменены позже в вашем приложении.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup parent = (ViewGroup) super.onCreateView( inflater, container, savedInstanceState );
View overlay = inflater.inflate( R.layout.view_map_overlay, parent, false );
mIndoorSelector = (SeekBar) overlay.findViewById( R.id.indoor_level_selector );
mIndoorMinLevel = (TextView) overlay.findViewById( R.id.indoor_min_level );
mIndoorMaxLevel = (TextView) overlay.findViewById( R.id.indoor_max_level );
parent.addView( overlay );
return parent;
}
|
Когда вы запустите приложение, вы должны увидеть свои виды в верхней части карты. Однако вы также по-прежнему увидите селектор уровня по умолчанию, который загромождает вид.

Чтобы это исправить, создайте новый метод с именем initMapIndoorSelector и вызовите его из onViewCreated . Все, что нужно сделать, это настроить слушателей на изменения SeekBar и уровня в помещении, а также отключить SeekBar уровня в помещении по умолчанию.
|
1
2
3
4
5
6
|
private void initMapIndoorSelector() {
mIndoorSelector.setOnSeekBarChangeListener( this );
getMap().getUiSettings().setIndoorLevelPickerEnabled( false );
getMap().setOnIndoorStateChangeListener( this );
}
|
Теперь, когда ваш вид наложен на карту, вы должны скрывать его, пока он не понадобится. В onViewCreated вызовите новый вспомогательный метод с именем hideFloorLevelSelector который скрывает все ваши наложенные представления.
|
1
2
3
4
5
|
private void hideFloorLevelSelector() {
mIndoorSelector.setVisibility( View.GONE );
mIndoorMaxLevel.setVisibility( View.GONE );
mIndoorMinLevel.setVisibility( View.GONE );
}
|
3. Использование внутреннего селектора уровня
Создав и скрыв свои виды, вы можете начать добавлять их в логику, чтобы они при необходимости отображались и взаимодействовали с картой. Ранее вы создали метод onIndoorBuildingFocused как часть GoogleMap.OnIndoorStateChangeListener . В этом методе вам нужно сохранить ссылку на то, какое здание находится в фокусе, а затем при необходимости скрыть или показать SeekBar управления SeekBar .
|
01
02
03
04
05
06
07
08
09
10
11
|
@Override
public void onIndoorBuildingFocused() {
mIndoorBuilding = getMap().getFocusedBuilding();
if( mIndoorBuilding == null || mIndoorBuilding.getLevels() == null || mIndoorBuilding.getLevels().size() <= 1 ) {
hideFloorLevelSelector();
} else {
showFloorLevelSelector();
}
}
|
Внутреннее здание получит фокус, когда здание будет видно камерой камеры, и карта будет достаточно увеличена. Если эти условия больше не выполняются, этот метод будет вызван снова и getMap().getFocusedBuilding вернет null значение.
showFloorLevelSelector делает showFloorLevelSelector все наложенные виды, перемещает SeekBar к правильному выбранному значению и устанавливает для текстовых меток значения, представляющие краткое имя верхнего и нижнего этажей для этого здания. Когда вы извлекаете уровни из объекта IndoorBuilding , нижний этаж является последним элементом в списке, а верхний этаж находится в позиции 0 .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
private void showFloorLevelSelector() {
if( mIndoorBuilding == null )
return;
int numOfLevels = mIndoorBuilding.getLevels().size();
mIndoorSelector.setMax( numOfLevels — 1 );
//Bottom floor is the last item in the list, top floor is the first
mIndoorMaxLevel.setText( mIndoorBuilding.getLevels().get( 0 ).getShortName() );
mIndoorMinLevel.setText( mIndoorBuilding.getLevels().get( numOfLevels — 1 ).getShortName() );
mIndoorSelector.setProgress( mIndoorBuilding.getActiveLevelIndex() );
mIndoorSelector.setVisibility( View.VISIBLE );
mIndoorMaxLevel.setVisibility( View.VISIBLE );
mIndoorMinLevel.setVisibility( View.VISIBLE );
}
|
Последний метод, который необходимо реализовать для селектора уровня внутри помещения, — это onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) . Когда позиция SeekBar изменяется, вам нужно активировать новый уровень в текущем здании. Поскольку уровни упорядочены сверху вниз, вам нужно активировать уровень в позиции numOfLevels - 1 - progress , чтобы соотнести его с позицией SeekBar .
|
1
2
3
4
5
6
7
8
|
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
if( mIndoorBuilding == null )
return;
int numOfLevels = mIndoorBuilding.getLevels().size();
mIndoorBuilding.getLevels().get( numOfLevels — 1 — progress ).activate();
}
|
4. Добавление просмотра улиц
Теперь, когда вы знаете, как накладывать виды на карту и как работать с селектором уровня в помещении, давайте рассмотрим, как работать с Street View в ваших приложениях. Просмотр улиц, как и Google Maps, позволяет использовать фрагмент или вид. В этом примере вы будете использовать StreetViewPanoramaView и наложить его на свой MapFragment .
Это представление будет инициализировано, чтобы показать улицу рядом с Мэдисон-Сквер-Гарден, и при длительном нажатии на другую область карты в режиме просмотра улиц будут отображаться изображения, связанные с выбранной позицией. Если вы выберете отображение области, которая не связана напрямую с изображением Street View, Google выберет ближайший для отображения, если он находится в пределах установленного расстояния. Если поблизости нет изображений Street View (скажем, вы выбрали место в середине океана), то Street View покажет черный экран.
Что еще нужно знать, это то, что вы можете иметь только один StreetViewPanoramaView или фрагмент, видимый пользователю одновременно.

Для начала обновите view_map_overlay.xml , чтобы добавить StreetViewPanoramaView .
|
1
2
3
4
5
|
<com.google.android.gms.maps.StreetViewPanoramaView
android:id=»@+id/steet_view_panorama»
android:layout_width=»match_parent»
android:layout_height=»240dp»
android:layout_alignParentBottom=»true»/>
|
Когда ваш файл макета будет готов, перейдите в onCreateView в вашем MapFragment , сохраните ссылку на ваш новый вид и вызовите метод onCreate для этого представления. Важно, чтобы вы вызывали onCreate , потому что текущий фрагмент onCreate уже был вызван до того, как этот вид был прикреплен, а компонент Street View выполняет в onCreate действия, необходимые для инициализации.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup parent = (ViewGroup) super.onCreateView( inflater, container, savedInstanceState );
View overlay = inflater.inflate( R.layout.view_map_overlay, parent, false );
mIndoorSelector = (SeekBar) overlay.findViewById( R.id.indoor_level_selector );
mIndoorMinLevel = (TextView) overlay.findViewById( R.id.indoor_min_level );
mIndoorMaxLevel = (TextView) overlay.findViewById( R.id.indoor_max_level );
mStreetViewPanoramaView = (StreetViewPanoramaView) overlay.findViewById(R.id.steet_view_panorama);
mStreetViewPanoramaView.onCreate(savedInstanceState);
parent.addView(overlay);
return parent;
}
|
Затем в onViewCreated добавьте новый метод initStreetView . Этот новый метод будет асинхронно получать объект StreetViewPanorama когда он будет готов, и обрабатывать, показывая начальную позицию Street View. Важно отметить, что getStreetViewPanoramaAsync( OnStreetViewPanoramaReadyCallback callback ) может вызываться только из основного потока.
|
01
02
03
04
05
06
07
08
09
10
11
|
private void initStreetView() {
getMap().setOnMapLongClickListener( this );
mStreetViewPanoramaView.getStreetViewPanoramaAsync(new OnStreetViewPanoramaReadyCallback() {
@Override
public void onStreetViewPanoramaReady(StreetViewPanorama panorama) {
mPanorama = panorama;
showStreetView( new LatLng( 40.7506, -73.9936 ) );
}
});
}
|
Наконец, вам нужно определить вспомогательный метод showStreetView( LatLng latlng ) показанный выше. Этот метод создает объект StreetViewPanoramaCamera который позволяет изменять наклон, масштаб и ориентацию камеры Street View. В этом примере для камеры установлены значения по умолчанию.
Далее вам нужно установить положение камеры. В этом примере мы также включаем необязательный параметр для отображения названий улиц.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
private void showStreetView( LatLng latLng ) {
if( mPanorama == null )
return;
StreetViewPanoramaCamera.Builder builder = new StreetViewPanoramaCamera.Builder( mPanorama.getPanoramaCamera() );
builder.tilt( 0.0f );
builder.zoom( 0.0f );
builder.bearing( 0.0f );
mPanorama.animateTo( builder.build(), 0 );
mPanorama.setPosition( latLng, 300 );
mPanorama.setStreetNamesEnabled( true );
}
|
Как только ваш showStreetView( LatLng latlng ) завершен, его также можно вызывать из onMapLongClick(LatLng latLng) чтобы вы могли легко изменить отображаемую область.
|
1
2
3
4
|
@Override
public void onMapLongClick(LatLng latLng) {
showStreetView( latLng );
}
|

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