对于国内的安卓开发者来说,对谷歌地图的对接应该是很少见的(国内墙了谷歌,而且国内的手机系统也几乎都是阉割版安卓系统),大家一般使用百度地图、高德地图进行地图开发。但是总有人会不幸的需要做国外市场的app,需要使用到谷歌地图,比如我。
鉴于网上很少有教程来详细讲解谷歌地图的对接,而我在入门了解的时候也是过程坎坷。在此记录下谷歌地图的一些简单东西,后续若还有接触会持续更新。
如果你使用的是国外的安卓手机,而并非国内各大厂商定制之后的阉割版安卓手机,那么恭喜你,你开发起来要方便很多。但是如果你身处国内,即使有国外的安卓手机,也还需要能。
本人也是小白,就不过多介绍了。本人使用过蓝灯免费版和影梭付费版,在手机上安装对应软件后开启,均能实现效果。国内的网络是无法强调用谷歌地图的api等接口的,所以,在进行调试开发的时候,手机必须能。
之前说了,因为谷歌在国内被墙的原因,国内的安卓手机,几乎都是阉割版安卓系统,Google Services、Google Store等都是没有的。而谷歌地图,需要手机内有这些应用,否则无法使用,如下图。
那么如何安装呢?按照网上的说法,要安装好几个谷歌应用,还说除了小米手机可以免root在小米手机商店安装谷歌安装器小米版,然后这个软件可以一次性自动安装好所有谷歌全家桶。
本人是使用本就root了的魅蓝手机,在魅族的应用商店内下载安装了谷歌安装器魅族专版,然后通过这个app安装好了谷歌全家桶。所以其他的不是很清楚。
另外,本人遇到的一个小插曲是,在安装好了谷歌全家桶之后,再次打开应用,还是出来了类似的提示。提示谷歌服务必须更新才能使用,提示文字的下方显示了一个更新按钮,在手机的情况下,点击更新,自动打开了谷歌商店的app,登录谷歌帐号,自动跳到了升级的界面,进行升级即可。
谷歌官方介绍
在手机里面有谷歌对应的服务与app,且手机能访问外网的情况下。按照如如上网址的谷歌官方文档的入门指南进行开发,且申请了密钥之后。app在手机内运行的时候能看到打开了地图,但是界面上没有任何可操作的东西(除了移动地图,放大缩小等),也没有定位到当前位置。这时候可以松口气了,集成的差不多了。
官方文档和支持,以及使用百度与谷歌搜索,可以解决我们大部分问题。所以这里也只是记录本人使用谷歌地图时使用到的一个东西,仅作记录用途。
根据入门介绍,已经在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也是如此)。
有一个现象就是,我用魅蓝手机测试的时候,打开定位的时候,会再次弹出是否使用谷歌定位服务,如果选择否,则还是无法获取到定位,用户必须选择同意使用谷歌定位服务。而在使用国外安卓手机做测试的时候,则没有这个弹框选择,开启定位的时候默认就是使用的谷歌定位服务。
类似于共享单车,在地图上需要在某些位置上添加标记。如下代码,可以为标记设置位置、图标、标题、描述内容等信息且添加到地图内。
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,不移動中心位置
}
});
一般情况下,我们点击某个标记或者位置,要进行导航的时候是跳转到谷歌地图,就如默认的方式那样。由地图进行专业的导航是最佳的,但是也许你也会遇到这样的奇葩需求,需要先在自己的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访问较慢。
目前大概了解且实现了,定位、添加标记、地图上画线表明如何从当前位置走到指定位置。主要代码如下(布局界面参考官方入门指南):
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,发现里面的谷歌地图能用了!!!神不神奇?? 可能是国内手机系统屏蔽了该谷歌定位服务?谷歌地图内部有东西能打开启用该服务?博主暂时还唔知……