谷歌地图对接记录(Android)

一、背景

对于国内的安卓开发者来说,对谷歌地图的对接应该是很少见的(国内墙了谷歌,而且国内的手机系统也几乎都是阉割版安卓系统),大家一般使用百度地图、高德地图进行地图开发。但是总有人会不幸的需要做国外市场的app,需要使用到谷歌地图,比如我。
鉴于网上很少有教程来详细讲解谷歌地图的对接,而我在入门了解的时候也是过程坎坷。在此记录下谷歌地图的一些简单东西,后续若还有接触会持续更新。

二、准备工作

如果你使用的是国外的安卓手机,而并非国内各大厂商定制之后的阉割版安卓手机,那么恭喜你,你开发起来要方便很多。但是如果你身处国内,即使有国外的安卓手机,也还需要能。

2.1

本人也是小白,就不过多介绍了。本人使用过蓝灯免费版和影梭付费版,在手机上安装对应软件后开启,均能实现效果。国内的网络是无法强调用谷歌地图的api等接口的,所以,在进行调试开发的时候,手机必须能。

2.2 谷歌大礼包安装

之前说了,因为谷歌在国内被墙的原因,国内的安卓手机,几乎都是阉割版安卓系统,Google Services、Google Store等都是没有的。而谷歌地图,需要手机内有这些应用,否则无法使用,如下图。
谷歌地图对接记录(Android)_第1张图片
那么如何安装呢?按照网上的说法,要安装好几个谷歌应用,还说除了小米手机可以免root在小米手机商店安装谷歌安装器小米版,然后这个软件可以一次性自动安装好所有谷歌全家桶。
本人是使用本就root了的魅蓝手机,在魅族的应用商店内下载安装了谷歌安装器魅族专版,然后通过这个app安装好了谷歌全家桶。所以其他的不是很清楚。
另外,本人遇到的一个小插曲是,在安装好了谷歌全家桶之后,再次打开应用,还是出来了类似的提示。提示谷歌服务必须更新才能使用,提示文字的下方显示了一个更新按钮,在手机的情况下,点击更新,自动打开了谷歌商店的app,登录谷歌帐号,自动跳到了升级的界面,进行升级即可。

2.3 谷歌地图嵌入

谷歌官方介绍
在手机里面有谷歌对应的服务与app,且手机能访问外网的情况下。按照如如上网址的谷歌官方文档的入门指南进行开发,且申请了密钥之后。app在手机内运行的时候能看到打开了地图,但是界面上没有任何可操作的东西(除了移动地图,放大缩小等),也没有定位到当前位置。这时候可以松口气了,集成的差不多了。

三、谷歌地图API使用

官方文档和支持,以及使用百度与谷歌搜索,可以解决我们大部分问题。所以这里也只是记录本人使用谷歌地图时使用到的一个东西,仅作记录用途。

3.1 定位到当前位置

根据入门介绍,已经在app内集成了谷歌地图,但是此时没有定位到当前位置的操作。可以看到代码里面有一个onMapReady的回调方法,当地图初始化之后,会进入该方法,我们可以在此方法内做一些基础操作,比如,显示地图自带的定位到当前位置的功能。

googleMap.setMyLocationEnabled(true); // 开启定位到当前位置的功能

在onMapReady方法内使用如上代码,再次运行app的时候,可以看到在地图右上角有一个定位按钮,点击之后就可以定位且显示到当前位置了。
因为项目中,定位当前位置的按钮要显示在左下角且要定制图标,而且在点击定位的同时还需要做其他操作,所以,我们这里虽然要开启定位功能,但是要隐藏右上角的图标,且要自定义按钮事件实现定位到当前位置。不显示右上角图片的代码如下。

googleMap.getUiSettings().setMyLocationButtonEnabled(false); // 右上角不显示定位图标

在这种情况下,没有按钮来点击定位到当前位置。我们需要做的是在进入app的时候就自动定位到当前位置。代码如下(也写在onMapReady方法内):

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

该方法有一个回调方法onConnected,在该方法内我们能拿到定位到的位置,然后调用api显示当前定位出来。如下代码,则打开app之后,定位到当前位置后会记录当前位置,且移动显示当前位置。

    @Override
    public void onConnected(Bundle bundle) {
        try {
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        if (mLastLocation != null) {
            latLng = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(latLng.latitude, latLng.longitude), 15));

            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("位置", " =============  ");
        }
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setInterval(5000); //5 seconds
        mLocationRequest.setFastestInterval(3000); //3 seconds
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

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

用户如果后续移动了地图,点击自定义的定位按钮时,我们使用mGoogleApiClient.reconnect();方法,就可以再次定位且显示到当前所在位置上了。

定位的时候,手机需要开启了定位服务。本人没找到谷歌地图app那样的直接打开定位的方法,所以只能在进行定位之前,判断当前是否开启了定位,没有的话给提示,跳转到设置里面让用户手动开启定位权限(国内大部分应用,包括ofo的app也是如此)。

有一个现象就是,我用魅蓝手机测试的时候,打开定位的时候,会再次弹出是否使用谷歌定位服务,如果选择否,则还是无法获取到定位,用户必须选择同意使用谷歌定位服务。而在使用国外安卓手机做测试的时候,则没有这个弹框选择,开启定位的时候默认就是使用的谷歌定位服务。

3.2 添加标记

类似于共享单车,在地图上需要在某些位置上添加标记。如下代码,可以为标记设置位置、图标、标题、描述内容等信息且添加到地图内。

mMap.addMarker(new MarkerOptions()
                .position(new LatLng(mLastLocation.getLatitude() + 0.0011111, mLastLocation.getLongitude() + 0.0011111))
                .title("title")
                .snippet("desc")
                .icon(BitmapDescriptorFactory.fromResource(R.mipmap.icon_marker_gray)));

标记有默认点击事件,点击的时候会在右下角弹出导航图标,点击之后,若手机内安装了谷歌地图会自动打开谷歌地图app进行导航,若手机内无谷歌地图app则会给出相应的提示。我们也可以自定义标记的点击事件,屏蔽掉默认行为。代码如下:

        mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
            @Override
            public boolean onMarkerClick(Marker marker) {
                // TODO
                return false; // 返回false,点击的时候会将点击标记移至界面中间;true,不移動中心位置
            }
        });

3.3 显示路线

一般情况下,我们点击某个标记或者位置,要进行导航的时候是跳转到谷歌地图,就如默认的方式那样。由地图进行专业的导航是最佳的,但是也许你也会遇到这样的奇葩需求,需要先在自己的app内也能显示过去的路线(自己app内集成谷歌地图如何进行导航包括语音,这我就不知道了,那也太变态了),那么如下工具类能帮到你(网上找的)。

public class GoogleMapUtils {
    /**
     * 通过起点终点,组合成url
     *
     * @param origin
     * @param dest
     * @return
     */
    public static String getDirectionsUrl(LatLng origin, LatLng dest) {

        // Origin of route
        String str_origin = "origin=" + origin.latitude + ","
                + origin.longitude;

        // Destination of route
        String str_dest = "destination=" + dest.latitude + "," + dest.longitude;

        // Sensor enabled
        String sensor = "sensor=false";

        // Travelling Mode
        String mode = "mode=driving";

        //waypoints,116.32885,40.036675
        String waypointLatLng = "waypoints=" + "40.036675" + "," + "116.32885";

        // Building the parameters to the web service
        String parameters = str_origin + "&" + str_dest/* + "&" + sensor + "&"
                + mode + "&" + waypointLatLng*/;

        // Output format
        String output = "json";

        // Building the url to the web service
        String url = "https://maps.googleapis.com/maps/api/directions/"
                + output + "?" + parameters;
        System.out.println("getDerectionsURL--->: " + url);
        return url;
    }

    /**
     * A method to download json data from url
     */
    public static String downloadUrl(String strUrl) throws IOException {
        String data = "";
        InputStream iStream = null;
        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(strUrl);

            // Creating an http connection to communicate with url
            urlConnection = (HttpURLConnection) url.openConnection();

            // Connecting to url
            urlConnection.connect();

            // Reading data from url
            iStream = urlConnection.getInputStream();

            BufferedReader br = new BufferedReader(new InputStreamReader(
                    iStream));

            StringBuffer sb = new StringBuffer();

            String line = "";
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }

            data = sb.toString();

            br.close();
        } catch (Exception e) {
            Log.e("Excep downloading url", e.toString());
        } finally {
            iStream.close();
            urlConnection.disconnect();
        }
        System.out.println("url:" + strUrl + "---->   downloadurl:" + data);
        return data;
    }

    // Fetches data from url passed
    @SuppressLint("NewApi")
    public static class DownloadTask extends AsyncTask<String, Void, String> {
        // Downloading data in non-ui thread
        @Override
        protected String doInBackground(String... url) {
            // For storing data from web service
            String data = "";
            try {
                // Fetching the data from web service
                data = downloadUrl(url[0]);
            } catch (Exception e) {
                Log.d("Background Task", e.toString());
            }
            return data;
        }

        // Executes in UI thread, after the execution of
        // doInBackground()
        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);

            ParserTask parserTask = new ParserTask();

            // Invokes the thread for parsing the JSON data
            parserTask.execute(result);
        }
    }

    /**
     * A class to parse the Google Places in JSON format
     */
    @SuppressLint("NewApi")
    public static class ParserTask extends
            AsyncTask>>> {

        // Parsing the data in non-ui thread
        @Override
        protected List>> doInBackground(
                String... jsonData) {

            JSONObject jObject;
            List>> routes = null;

            try {
                jObject = new JSONObject(jsonData[0]);
                DirectionsJSONParser parser = new DirectionsJSONParser();

                // Starts parsing data
                routes = parser.parse(jObject);
                System.out.println("do in background:" + routes);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return routes;
        }

        // Executes in UI thread, after the parsing process
        @Override
        protected void onPostExecute(List>> result) {
            ArrayList points = null;
            PolylineOptions lineOptions = null;
            MarkerOptions markerOptions = new MarkerOptions();

            // Traversing through all the routes
            if (null != result) {
                for (int i = 0; i < result.size(); i++) {
                    points = new ArrayList();
                    lineOptions = new PolylineOptions();

                    // Fetching i-th route
                    List> path = result.get(i);

                    // Fetching all the points in i-th route
                    for (int j = 0; j < path.size(); j++) {
                        HashMap point = path.get(j);

                        double lat = Double.parseDouble(point.get("lat"));
                        double lng = Double.parseDouble(point.get("lng"));
                        LatLng position = new LatLng(lat, lng);

                        points.add(position);
                    }

                    // Adding all the points in the route to LineOptions
                    lineOptions.addAll(points);
                    lineOptions.width(Util.dp2px(LBoxApplication.getContext(), 3));

                    // Changing the color polyline according to the mode
                    lineOptions.color(Color.parseColor("#4b98ff"));
                }

                // Drawing polyline in the Google Map for the i-th route
                MainActivity.mMap.addPolyline(lineOptions);
            }
        }
    }
}

使用的时候只需要传值两个位置就行,表明从这个位置到另一个位置。

new GoogleMapUtils.DownloadTask().execute(GoogleMapUtils.getDirectionsUrl(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()),
                    new LatLng(mLastLocation.getLatitude() + 0.00222222, mLastLocation.getLongitude() - 0.00222222)));

该工具类的原理是调用谷歌的api接口,根据某些参数查询路线(代码中我注释掉了一些参数,使用默认值也就是步行的最优方案),然后根据查到的路线的结果调用googleMap.addPolyline方法,设置好线的颜色、宽度等即可。在本地测试的时候发现该api访问较慢。

3.4 主界面的代码

目前大概了解且实现了,定位、添加标记、地图上画线表明如何从当前位置走到指定位置。主要代码如下(布局界面参考官方入门指南):

public class MainActivity extends BaseActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
        LocationListener, OnMapReadyCallback {

    private Location mLastLocation = null;
    public static GoogleMap mMap;
    public static boolean showRoute = false;

    Marker mCurrLocation;
    LatLng latLng;
    LocationRequest mLocationRequest;
    GoogleApiClient mGoogleApiClient;

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

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

    @OnClick({R.id.location_iv})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.location_iv:
                // 重新定位,会重新获取附近充电宝实况
                mGoogleApiClient.reconnect();
                break;
        }
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;

        mMap.setMyLocationEnabled(true);
        mMap.getUiSettings().setMyLocationButtonEnabled(false); // 右上角不显示定位图标

        mMap.getUiSettings().setMapToolbarEnabled(false); // 点击标记底部右下角不出来控件

        mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
            @Override
            public boolean onMarkerClick(Marker marker) {
                // TODO
                return false; // 返回false,点击的时候会将点击标记移至界面中间;true,不移動中心位置
            }
        });

        buildGoogleApiClient();

        mGoogleApiClient.connect();
    }

    private void addMarkersToMap() {
        mMap.addMarker(new MarkerOptions()
                .position(new LatLng(mLastLocation.getLatitude() + 0.0011111, mLastLocation.getLongitude() + 0.0011111))
                .title("title")
                .snippet("desc")
                .icon(BitmapDescriptorFactory.fromResource(R.mipmap.icon_marker_gray)));
    }

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

        if (showRoute) {
            new GoogleMapUtils.DownloadTask().execute(GoogleMapUtils.getDirectionsUrl(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()),
                    new LatLng(mLastLocation.getLatitude() + 0.00222222, mLastLocation.getLongitude() - 0.00222222)));
            showRoute = false;
        }
    }

    /**
     * * 离开界面时,断开连接
     */
    @Override
    protected void onStop() {
        super.onStop();
        if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @SuppressLint("NewApi")
    @Override
    public void onConnected(Bundle bundle) {
        try {
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        if (mLastLocation != null) {
            latLng = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
            mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(latLng.latitude, latLng.longitude), 15));

            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("位置", " =============  ");
        }
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setInterval(5000); //5 seconds
        mLocationRequest.setFastestInterval(3000); //3 seconds
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

        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();
    }

    /**
     * 当位置发生改变
     **/
    @Override
    public void onLocationChanged(Location location) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }
}

——20180112
小编又再次来折腾了一回谷歌地图,这次小编手里只有一台华为测试机,老规矩,安装了软件,安装了谷歌商店,然后谷歌商店中下载安装了GooglePlay服务和谷歌服务框架。然后,发现改善无法使用该谷歌地图定位。调试代码发现不支持谷歌位置服务,设置中查找定位服务,没看到谷歌位置服务相关的东西。无果,在博主一位前同事的建议下,安装了谷歌地图APP,打开谷歌地图,定位时会弹出框要求给定位权限,其中提及到使用谷歌位置服务,此时再去打开要调试的APP,发现里面的谷歌地图能用了!!!神不神奇?? 可能是国内手机系统屏蔽了该谷歌定位服务?谷歌地图内部有东西能打开启用该服务?博主暂时还唔知……

你可能感兴趣的:(Android,Android笔记)