Вступление
Одна из самых полезных функций для пользователей — интеграция карт. В предыдущей части этой серии мы обсуждали, как настроить 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 и о том, как использовать ее для добавления кластеров маркеров, тепловых карт и других полезных функций для ваших приложений.