本篇囊括了对GoogleMap Android SDK的安装使用和对一部分API和其使用方式的介绍,只写了自己知道的,反正关于GoogleMap,看这一篇肯定不够
此外,这里使用的版本是16.1.0,目前的最新版本是17.0.0,可能会有变化,我可能看情况更新
注意:没有API Key的话地图是无法正常显示的,此外,没有GMS地图也只会显示让你更新GMS和一个update的跳转按钮。
此外,鉴于本人莫得感情,更莫得钱,所以本文只包括免费的部分
我先贴一个介绍到的API列表(并不会全都详细介绍,以及有一些我自己还没用过的API我就不写了),如果有感兴趣的可以自行往下翻:
API | Description |
---|---|
GoogleMap | |
animateCamera | 调整镜头位置,设置焦距等 |
setIndoorEnabled | 是否支持屋内地图 |
setBuildingsEnabled | 是否支持立体建筑图 |
setTrafficEnabled | 是否支持交通图 |
setMapToolbarEnabled | 是否显示右下角的两个Google的Toolbar |
moveCamera | 移动镜头到指定坐标 |
setMaxZoomPreference | 设置最大焦距,设置之后再设置焦距时默认不会超出这个范围 |
setMinZoomPreference | 设置最小焦距,设置之后再设置焦距时默认不会超出这个范围 |
setRotateGesturesEnabled | 是否支持旋转手势 |
setScrollGesturesEnabled | 是否支持滑动手势 |
setZoomGesturesEnabled | 是否支持缩放手势,包括双击放大和双指放大缩小 |
getProjection | 获取投影 |
API | Description |
---|---|
Marker | |
addMarker | 在地图上创建一个Marker,同时返回一个Marker对象 |
position | 即放置的位置坐标 |
anchor | Marker显示的位置,默认是(0.5f, 1),代表Marker图标的下方中间点显示在地图的指定坐标点 |
zIndex | 多个Marker重叠时控制哪个显示在上面,值越大越会显示在上层 |
icon | 自定义Marker的图标,参数需要是GoogleMap库里面的BitmapDiscriptor形式 |
title | Marker的标题 |
snippet | 和title差不多,显示在其下面 |
visible | 可见性 |
BitmapDiscriptorFactory | |
fromBitmap | 使用一个Bitmap对象来创建一个BitmapDiscriptor |
fromAsset | 从Asset文件中选文件来创建bitmap |
fromFile | 使用内部存储中的文件名 |
fromPath | 使用绝对文件路径 |
fromResource | 从res文件中创建bitmap |
shapes和groundOverlay我感觉差不太多,放一起吧
API | Descrpition |
---|---|
CircleOptions | |
center | 用坐标来设置中心点 |
radius | 半径,单位为米 |
Circle.setCenter | 用来动态调整中心点 |
Circle.setRadius | 用来动态调整半径 |
clickable | 是否可以点击,可以用map的setOncircleClickListener来监听点击事件 |
Circle.setClickable | 用来动态设置是否可以点击 |
strokeWidth | 描边半径,单位为像素(px) |
fillColor | 填充颜色(填充是被单位为米的半径控制的范围) |
strokeColor | 描边颜色,关于颜色,使用资源文件里定义的颜色应该是无效的,自己写颜色的数值或者使用Color类中的那些。 |
以上这些和UI有关的控件应该都可以通过setTag来绑定数据
此外,包括一些其他问题:
- 一些其他的更的API
- 如何计算ZoomLevel和屏幕像素的比例
- 如何判断是否开启GMS,以及主动尝试更新GMS
- 如何尝试做些简单的动画
- 一些坑(动画,手势,MapView的Load,部分机型下Crash等)
对于这部分内容还会再写一篇
那么我们开始吧:
1. 使用方式
Google Map 安卓端免费使用的方式有两种:
- SupportMapFragment:占据全屏,创建后使用即可
public class GoogleMapActivity extends BaseToolbarActivity implements OnMapReadyCallback {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
if (mapFragment != null) {
try {
// map相关操作全都有可能抛Exception,以防万一最好都catch住
mapFragment.getMapAsync(this);
mapFragment.onResume();
} catch (RuntimeRemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onMapReady(GoogleMap googleMap) {
// 这里返回创建好的map
}
}
- MapView:使用更加灵活,但是需要主动维护生命周期
MapView mapView = findViewById(R.id.map);
mapView.onCreate(null);
mapView.getMapAsync(new OnMapReadyCallback() {
@Override
public void onMapReady(GoogleMap googleMap) {
// 这里返回创建好的map
}
});
注意:如果不想维护MapView的生命周期,可以在布局文件中定义LiteMode, 也可以在代码中定义:
布局文件中:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:name="com.google.android.gms.maps.MapFragment"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraZoom="13"
map:mapType="normal"
map:liteMode="true"/>
<!-- MapView同样是这个字段 -->
代码中:
GoogleMapOptions options = new GoogleMapOptions().liteMode(true);
关于在ListView
或者RecyclerView
中使用MapView:
下面这部分代码我懒得自己写例子了,来自上面的官方Demo
简单说一下就是:在类似onCreate的时机将MapView初始化:
// 可以看到,官方是在ViewHolder的构造函数里进行的初始化,即其创建的时候,创建完后加载一下地图内容
private ViewHolder(View itemView) {
super(itemView);
layout = itemView;
mapView = layout.findViewById(R.id.lite_listrow_map);
title = layout.findViewById(R.id.lite_listrow_text);
if (mapView != null) {
// Initialise the MapView
mapView.onCreate(null);
// Set the map ready callback to receive the GoogleMap object
mapView.getMapAsync(this);
}
}
@Override
public void onMapReady(GoogleMap googleMap) {
MapsInitializer.initialize(getApplicationContext());
map = googleMap;
setMapLocation();
}
然后,不管是ListView还是RecyclerView,实现RecyclerListener
private RecyclerView.RecyclerListener mRecycleListener = new RecyclerView.RecyclerListener() {
@Override
public void onViewRecycled(RecyclerView.ViewHolder holder) {
MapAdapter.ViewHolder mapHolder = (MapAdapter.ViewHolder) holder;
if (mapHolder != null && mapHolder.map != null) {
// Clear the map and free up resources by changing the map type to none.
// Also reset the map when it gets reattached to layout, so the previous map would
// not be displayed.
mapHolder.map.clear();
mapHolder.map.setMapType(GoogleMap.MAP_TYPE_NONE);
}
}
};
这样item被回收的时候会调用到这里,clear会清理掉marker之类的控件,type设置成none会释放资源。再在类似onBind这种重新加载的时机,调用如下方法重新设置图标和地图样式之类的。
private void setMapLocation() {
if (map == null) return;
NamedLocation data = (NamedLocation) mapView.getTag();
if (data == null) return;
// Add a marker for this item and set the camera
map.moveCamera(CameraUpdateFactory.newLatLngZoom(data.location, 13f));
map.addMarker(new MarkerOptions().position(data.location));
// Set the map type back to normal.
map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
}
private void bindView(int pos) {
NamedLocation item = namedLocations[pos];
// Store a reference of the ViewHolder object in the layout.
layout.setTag(this);
// Store a reference to the item in the mapView's tag. We use it to get the
// coordinate of a location, when setting the map location.
mapView.setTag(item);
setMapLocation();
title.setText(item.name);
}
}
2. 关于Marker
简单来说,这么用就行:
LatLng userPos = new LatLng(0, 0);
userMarker = currentMap.addMarker(new MarkerOptions()
.position(userPos)
.anchor(0.5f, 0.5f)
.zIndex(1)
.icon(BitmapDescriptorFactory.fromResource(
R.drawable.ic_google_map_blue_point)));
anchor(0.5f, 0.5f)
表示这个Marker图标的正中心会显示在position上面
zIndex(1)
表示它会显示在zIndex为0的Marker上面
此外,icon里面使用的资源需要是静态的,因为要转换成bitmap
关于Marker里面的InfoWindow
:
InfoWindow是显示在Marker上方的,举例:
自定义一个InfoWindow:
public class MyInfoAdapter implements GoogleMap.InfoWindowAdapter {
private View mWindow;
MyInfoAdapter() {
mWindow = getLayoutInflater().inflate(R.layout.layout_map_info_window,
new RelativeLayout(getContext()), false);
}
@Override
public View getInfoWindow(Marker marker) {
return mWindow;
}
@Override
public View getInfoContents(Marker marker) {
return null;
}
}
添加到地图和显示:
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setInfoWindowAdapter(new MyInfoAdapter());
mMarker.showInfoWindow();
mMarker.hideInfoWindow();
}
可见,添加到地图是Map里面的方法,而显示和隐藏是Marker里面的方法。
在添加多个Marker的时候,最好注意控制InfoWindow显示在哪个Marker上面。
默认情况下,点击哪个Marker,InfoWindow就会显示在哪个Marker上面
此外,InfoWindow显示的内容和Marker一样都是静态的,如果是个需要加载时间的图片,最好想办法过后再重新使用showInfoWindow显示一次,重新显示时会刷新。
3. 关于animateCamera()
GoogleMap里面,想要移动镜头有几种方式:
mMap.moveCamera()
mMap.animateCamera()
主要区别是在于是否有动画效果,move自然是立刻生效的
这里懒得多写了,直接拿官方的例子
private static final LatLng SYDNEY = new LatLng(-33.88,151.21);
private static final LatLng MOUNTAIN_VIEW = new LatLng(37.4, -122.1);
private GoogleMap map;
... // Obtain the map from a MapFragment or MapView.
// Move the camera instantly to Sydney with a zoom of 15.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(SYDNEY, 15));
// Zoom in, animating the camera.
map.animateCamera(CameraUpdateFactory.zoomIn());
// Zoom out to zoom level 10, animating with a duration of 2 seconds.
map.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);
// Construct a CameraPosition focusing on Mountain View and animate the camera to that position.
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(MOUNTAIN_VIEW) // Sets the center of the map to Mountain View
.zoom(17) // Sets the zoom
.bearing(90) // Sets the orientation of the camera to east
.tilt(30) // Sets the tilt of the camera to 30 degrees
.build(); // Creates a CameraPosition from the builder
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
可见moveCamera和animateCamera的参数CameraUpdate
基本上都是由CameraUpdateFactory
这个工厂类来创建的,其中animate能够设置地址,设置持续时间,回调等。
对镜头的控制包括位置和焦距,可以通过LatLng和ZoomLevel控制
回调包括onFinish()
和onCancel()
,一个是动画正常结束,另一种是中断
4. 关于onMapLoadedCallback
currentMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
@Override
public void onMapLoaded() {
try {
... // do something
} catch (RuntimeRemoteException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
}
});
这个OnMapLoadedCallback()
会在地图完全加载完成时被调用
5. 关于Exception
然后上面有一些关于Exception的处理,实际上,使用GoogleMap类的任何方法都有可能抛出RuntimeRemoteException
,最好做一下处理
网友评论