Android GoogleMap不完全指南 (二)

Android Google Map/谷歌地图 接入不完全指南 (一)已良好运行起官方demo和说明注意事项。

一、GoogleMap

谷歌地图使用起来大概分两种方式
1、以fragment的方式使用
2、以控件的方式,也就是原生的GoogleMap

发现,现在google家的地图现在已经有比较全的官方中文文档啦,官方文档是个好东西,我这里小文,只是根据其衍生的出来的东西,毕竟“曾见郭象注庄子,却是庄子注郭象”嘛。
原著就是叼。

一.1 以fragment的方式使用googleMap

亦可参见源例

image.png

.

示例代码
SimpleFragmentModeActivity

public class SimpleFragmentModeActivity extends AppCompatActivity implements OnMapReadyCallback {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_mode);

        SupportMapFragment mapFragment =
                (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {

        double lat = 0.0;
        double lng = 0.0;
        LatLng appointLoc = new LatLng(lat, lng);

        // 移动地图到指定经度的位置
        googleMap.moveCamera(CameraUpdateFactory.newLatLng(appointLoc));

        //添加标记到指定经纬度
        googleMap.addMarker(new MarkerOptions().position(new LatLng(lat, lng)).title("Marker")
                .icon(BitmapDescriptorFactory.fromResource(R.drawable.chat_loc_point)));

        /*// 点击标记点,默认点击弹出跳转google地图或导航选择,返回true则不弹出
        googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
            @Override
            public boolean onMarkerClick(Marker marker) {
                return true;
            }
        });

        // 单击
        googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
            @Override
            public void onMapClick(LatLng latLng) {

            }
        });

        // 不允许手势缩放
        googleMap.getUiSettings().setZoomGesturesEnabled(false);
        //googleMap.getUiSettings().setMapToolbarEnabled(false); 禁用精简模式下右下角的工具栏

        // 不允许拖动地图
        googleMap.getUiSettings().setScrollGesturesEnabled(false);

        // 设置缩放级别
        CameraUpdate zoom = CameraUpdateFactory.zoomTo(13);
        googleMap.animateCamera(zoom);*/
    }
}

.
.
对应布局

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          class="com.google.android.gms.maps.SupportMapFragment" />

.
.
使用步骤

  • 第一步
    布局为文件放上fragment,注意class="com.google.android.gms.maps.SupportMapFragment"不可或缺

  • 第二步
    得到SupportMapFragment对象,调用getMapAsync异步读取地图

  • 第三步,在getMapAsync的OnMapReadyCallback接口中的onMapReady方法中指定经纬度和marker。
    .
    默认效果

默认效果
1、可双指缩放
2、可单指移动
3、带有指南针的,
4、maker点是点击的,点击后弹出toolbar(此toolbar是地图的toobar,即跳转googlemap app或者导航)
5、双击放大

我们可以获取UiSettings或者利用googleMap对象直接设置。
代码的备注里面我也简单列举了一些,可以参见官网文档进行调整。

如果不需要这些东西,就要特别简单的地图,那么我们可以使用精简模式。

精简模式

如果我们不想要默认的效果,那么我们完全可以使用精简模式。

  • 使用方式

1、fragment布局代码中 map:liteMode="true"
2、java代码里面配置 GoogleMapOptions options = new GoogleMapOptions().liteMode(true);

image.png
  • 精简模式与默认的地图toolbar
    我们使用精简模式之后,右下角的地图的Toolbar自己就跑出来了,我们可以通过UiSettings().setMapToolbarEnabled(false);把Toolbar禁掉,就不会出现了。

如果你说为什么使用精简模式之后地图不是居中了,那时因为缩放关系导致的,你只要把缩放级别调整一下就好

        // 设置缩放级别
        CameraUpdate zoom = CameraUpdateFactory.zoomTo(13);
        googleMap.animateCamera(zoom);

zoomTo(float),因为默认的太小,看到是世界地图,只要放大到可以铺满屏幕,大概3-5之间吧,就不会出现上下空白仅有中间的情况。

嗯,fragment大概到这里。

一.2 MapView

亦可参见源例
.
.
MapView

MapView 是 Android View 类的一个子类, 用于在 Android View 中放置地图。 View 表示屏幕的某个矩形区域, 是 Android 应用和小工具的基本构建基块。 MapView 与 MapFragment 很相似,它也充当地图容器,通过 GoogleMap 对象公开核心地图功能。

在完全交互模式下使用该 API 时,MapView 类的用户必须将下列 Activity 生命周期方法转发给 MapView 类中的相应方法:onCreate()、onStart()、onResume()、onPause()、onStop()、onDestroy()、onSaveInstanceState() 和 onLowMemory()。

例子

image.png

.
SimpleMapActivity

public class SimpleMapActivity extends AppCompatActivity implements OnMapReadyCallback {

    private MapView mMapView;

    private static final String MAPVIEW_BUNDLE_KEY = "MapViewBundleKey";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_raw_map);

        // *** IMPORTANT ***
        // MapView requires that the Bundle you pass contain _ONLY_ MapView SDK
        // objects or sub-Bundles.
        Bundle mapViewBundle = null;
        if (savedInstanceState != null) {
            mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY);
        }
        mMapView = (MapView) findViewById(R.id.mMapView);
        mMapView.onCreate(mapViewBundle);

        mMapView.getMapAsync(this);
    }

    @Override
    public void onMapReady(GoogleMap map) {
        double lat = 39.937795;
        double lng = 116.387224;
        LatLng appointLoc = new LatLng(lat, lng);

        map.addMarker(new MarkerOptions().position(appointLoc).title("Marker"));
        map.moveCamera(CameraUpdateFactory.newLatLng(appointLoc));
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        Bundle mapViewBundle = outState.getBundle(MAPVIEW_BUNDLE_KEY);
        if (mapViewBundle == null) {
            mapViewBundle = new Bundle();
            outState.putBundle(MAPVIEW_BUNDLE_KEY, mapViewBundle);
        }

        mMapView.onSaveInstanceState(mapViewBundle);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mMapView.onResume();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mMapView.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mMapView.onStop();
    }



    @Override
    protected void onPause() {
        mMapView.onPause();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        mMapView.onDestroy();
        super.onDestroy();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mMapView.onLowMemory();
    }
}

.
.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Raw Map"
        android:textSize="30sp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        />

    <com.google.android.gms.maps.MapView
        android:id="@+id/mMapView"
        android:layout_width="200dp"
        android:layout_height="150dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:background="#66ff0000"
        >
    </com.google.android.gms.maps.MapView>
</LinearLayout>

.
.
MapView的大概就到这里,声明周期对于MapView很重要。


二、自动获取经纬度和逆地理编码

定位,一种是官方的My Location,主要使用方式是 mMap.setMyLocationEnabled(true);
My Location官方文档 位置数据
My Location demo

但是在这种方式只是地图层面地获取“我的位置”,My Location 层不会返回任何数据,这往往不是我们要的,所以这里略过,想了解可以看上面的两个链接。


FusedLocationProviderApi

编码获取位置现在比较好的方式是使用 [FusedLocationProviderApi ],因为它提供了比旧式的LocationManager API更加方便更加省电的方式。另外,如果你已经在谷歌地图上使用谷歌Play服务,就没有理由不使用它了。

使用FusedLocationProviderApi,就需要在gradle引入play-services,比如

   compile 'com.google.android.gms:play-services:10.2.6'

而引入play-services,可能报multiDex
那么我们需要配置一下主mudule的gradle

image.png
// 添加之
multiDexEnabled true

// 添加之
dexOptions {
    incremental true
    javaMaxHeapSize "4g"
}

一般即可解决。

接下来我们来写一个demo,功能是
进入页面自动获取当前经纬度,拿到经纬度之后进行逆地理编码,得到地址

获取当前位置经纬度

我们要利用FusedLocationApi获取当前位置,其实核心就是调用下面这几行代码,

mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

addConnectionCallbacks

而这个build的过程最主要的就是addConnectionCallbacks方法,传入的GoogleApiClient.ConnectionCallbacks接口要求我们实现如下两个方法

  • 1、onConnected 异步进行连接,获取位置 (关键方法)
  • 2、onConnectionSuspended 当客户端断开
    .
    .

addOnConnectionFailedListener

其他的,addOnConnectionFailedListener传入的GoogleApiClient.OnConnectionFailedListener实现的onConnectionFailed方法当然是连接失败时调用,这点没其他特别的。

gif

myloc.gif
public class SimpleMyLocActivity extends AppCompatActivity implements

        OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

    LocationRequest mLocationRequest;
    GoogleApiClient mGoogleApiClient;

    LatLng latLng;
    GoogleMap mGoogleMap;
    SupportMapFragment mMapFragment;
    Marker mCurrLocation;

    private static final int REQUESTCODE = 6001;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_loc);

        mMapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
        mMapFragment.getMapAsync(this);

    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mGoogleMap = googleMap;
        // 允许获取我的位置
        try {
            mGoogleMap.setMyLocationEnabled(true);
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        buildGoogleApiClient();
        mGoogleApiClient.connect();
    }

    @Override
    public void onPause() {
        super.onPause();
        //Unregister for location callbacks:
        if (mGoogleApiClient != null) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, new LocationCallback() {
                @Override
                public void onLocationResult(LocationResult locationResult) {
                    super.onLocationResult(locationResult);
                }
            });
        }
    }

    protected synchronized void buildGoogleApiClient() {
        Toast.makeText(this, "buildGoogleApiClient", Toast.LENGTH_SHORT).show();
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    public void onConnected(Bundle bundle) {
        Toast.makeText(this, "onConnected", Toast.LENGTH_SHORT).show();
        Location mLastLocation = null;
        try {
            Log.i("位置", LocationServices.FusedLocationApi.getLocationAvailability(mGoogleApiClient) + "");
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        } catch (SecurityException e) {
            e.printStackTrace();
        }


        if (mLastLocation != null) {
            //place marker at current position
            mGoogleMap.clear();
            latLng = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
            MarkerOptions markerOptions = new MarkerOptions();
            markerOptions.position(latLng);
            markerOptions.title("Current Position");
            mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()), 15));
            // 添加marker,但是这里我们特意把marker弄成透明的
            markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.chat_loc_point));

            mCurrLocation = mGoogleMap.addMarker(markerOptions);

            Log.i("位置", mLastLocation + "1111111");
            Log.i("位置", "最新的位置 getProvider " + mLastLocation.getProvider());
            Log.i("位置", "最新的位置 getAccuracy " + mLastLocation.getAccuracy());
            Log.i("位置", "最新的位置 getAltitude " + mLastLocation.getAltitude());
            Log.i("位置", "最新的位置 Bearing() " + mLastLocation.getBearing());
            Log.i("位置", "最新的位置 Extras() " + mLastLocation.getExtras());
            Log.i("位置", "最新的位置 Latitude() " + mLastLocation.getLatitude());
            Log.i("位置", "最新的位置 Longitude()() " + mLastLocation.getLongitude());
            Log.i("位置", " =============  ");
            TextView mTvAddress = (TextView) findViewById(R.id.mTvAddress);
            String address = getAddress(SimpleMyLocActivity.this, mLastLocation.getLatitude(), mLastLocation.getLongitude());
            mTvAddress.setText(address);
        }
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setInterval(5000); //5 seconds
        mLocationRequest.setFastestInterval(3000); //3 seconds
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        //mLocationRequest.setSmallestDisplacement(0.1F); //1/10 meter

        //LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);
            }
        });
    }

    @Override
    public void onConnectionSuspended(int i) {
        Toast.makeText(this, "onConnectionSuspended", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Toast.makeText(this, "onConnectionFailed", Toast.LENGTH_SHORT).show();
    }


    /**
     * 逆地理编码 得到地址
     * @param context
     * @param latitude
     * @param longitude
     * @return
     */
    public static String getAddress(Context context, double latitude, double longitude) {
        Geocoder geocoder = new Geocoder(context, Locale.getDefault());
        try {
            List<android.location.Address> address = geocoder.getFromLocation(latitude, longitude, 1);
            Log.i("位置", "得到位置当前" + address + "'\n"
                    + "经度:" + String.valueOf(address.get(0).getLongitude()) + "\n"
                    + "纬度:" + String.valueOf(address.get(0).getLatitude()) + "\n"
                    + "纬度:" + "国家:" + address.get(0).getCountryName() + "\n"
                    + "城市:" + address.get(0).getLocality() + "\n"
                    + "名称:" + address.get(0).getAddressLine(1) + "\n"
                    + "街道:" + address.get(0).getAddressLine(0)
            );
            return address.get(0).getAddressLine(0) + "  " + address.get(0).getLocality() + " " + address.get(0).getCountryName();
        } catch (Exception e) {
            e.printStackTrace();
            return "未知";
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case REQUESTCODE: {
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    buildGoogleApiClient();
                    mGoogleApiClient.connect();
                } else {
                }
                return;
            }
        }
    }
}

.
.
.
布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:map="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <RelativeLayout
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:layout_weight="1">
        <fragment xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:id="@+id/map"
                  android:name="com.google.android.gms.maps.SupportMapFragment"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  tools:context="com.gmapdemo2.googlemapdemosimple.MapsActivity"
                  android:layout_alignParentLeft="true"
                  android:layout_alignParentStart="true" >
        </fragment>
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerInParent="true"
            android:layout_centerVertical="true"
            android:src="@drawable/poi_marker_pressed"/>
    </RelativeLayout>
    
    <TextView
        android:id="@+id/mTvAddress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="20dp"
        tools:text="123123"
        android:background="#ffffff"
        android:gravity="center"
        android:textColor="#0000ff"
        android:paddingTop="20dp"
        />
</LinearLayout>

最后把流程画一下

GoogleMap地理编码与逆地理编码.png

核心代码在此,根据上面的代码,即可实现自动定位,获取经纬度并且得到地址位置信息。
当前地点为东南亚,经测试,安卓5.0,6.0和7.1均可良好运行,其他国家地区应该也可以。大天朝除外。

二、小细节

1、在google控制台创建项目默认是不开启包括地图在内的任何api的,请开启对应功能,不然无法显示地图,开启方法请看Android Google Map/谷歌地图 接入不完全指南 (一)

2、android API23 以上请做好权限申请工作,避免因为权限问题导致的闪退。

.

本篇大概多少到这里,下一遍是关于列表的
Android GoogleMap不完全指南 (三)

本文完。

三、链接

小demo git链接

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

推荐阅读更多精彩内容