美文网首页Android
GoogleMap Android SDK介绍(1),从入门到出

GoogleMap Android SDK介绍(1),从入门到出

作者: 景山道人 | 来源:发表于2019-06-23 16:50 被阅读0次

官方API Demo

官方文档

注册API Key

本篇囊括了对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来绑定数据

此外,包括一些其他问题:

  1. 一些其他的更的API
  2. 如何计算ZoomLevel和屏幕像素的比例
  3. 如何判断是否开启GMS,以及主动尝试更新GMS
  4. 如何尝试做些简单的动画
  5. 一些坑(动画,手势,MapView的Load,部分机型下Crash等)

对于这部分内容还会再写一篇

那么我们开始吧:

1. 使用方式

Google Map 安卓端免费使用的方式有两种:

  1. 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
    }
    
}
  1. 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了解一下

下面这部分代码我懒得自己写例子了,来自上面的官方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里面,想要移动镜头有几种方式:

  1. mMap.moveCamera()
  2. 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,最好做一下处理

相关文章

网友评论

    本文标题:GoogleMap Android SDK介绍(1),从入门到出

    本文链接:https://www.haomeiwen.com/subject/wjqtqctx.html