@RitcheeQinG
2020-03-23T08:31:20.000000Z
字数 7504
阅读 504
Android
本篇囊括了对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来绑定数据
此外,包括一些其他问题:
对于这部分内容还会再写一篇
那么我们开始吧:
Google Map 安卓端免费使用的方式有两种:
public class GoogleMapActivity extends BaseToolbarActivity implements OnMapReadyCallback {@Overrideprotected 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();}}}@Overridepublic void onMapReady(GoogleMap googleMap) {// 这里返回创建好的map}}
MapView mapView = findViewById(R.id.map);mapView.onCreate(null);mapView.getMapAsync(new OnMapReadyCallback() {@Overridepublic 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 MapViewmapView.onCreate(null);// Set the map ready callback to receive the GoogleMap objectmapView.getMapAsync(this);}}@Overridepublic void onMapReady(GoogleMap googleMap) {MapsInitializer.initialize(getApplicationContext());map = googleMap;setMapLocation();}
然后,不管是ListView还是RecyclerView,实现RecyclerListener
private RecyclerView.RecyclerListener mRecycleListener = new RecyclerView.RecyclerListener() {@Overridepublic 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 cameramap.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);}}
简单来说,这么用就行:
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);}@Overridepublic View getInfoWindow(Marker marker) {return mWindow;}@Overridepublic View getInfoContents(Marker marker) {return null;}}
添加到地图和显示:
@Overridepublic 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显示一次,重新显示时会刷新。
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 buildermap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
可见moveCamera和animateCamera的参数CameraUpdate基本上都是由CameraUpdateFactory这个工厂类来创建的,其中animate能够设置地址,设置持续时间,回调等。
对镜头的控制包括位置和焦距,可以通过LatLng和ZoomLevel控制
回调包括onFinish()和onCancel(),一个是动画正常结束,另一种是中断
currentMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {@Overridepublic void onMapLoaded() {try {... // do something} catch (RuntimeRemoteException e) {e.printStackTrace();} catch (NullPointerException e) {e.printStackTrace();}}});
这个OnMapLoadedCallback()会在地图完全加载完成时被调用
然后上面有一些关于Exception的处理,实际上,使用GoogleMap类的任何方法都有可能抛出RuntimeRemoteException,最好做一下处理