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

官方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,最好做一下处理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容