-
- 简介
- 获取自己的位置LocationManager
- LocationManager 的基本用法
- 实例
- 反向地理编码看懂位置信息经纬度到具体位置
- 直接使用Geocoding API
- 实例
- 使用百度地图
- 实践
- 使用覆盖物来增加更多功能
- 常用覆盖物MyLocationOverlay标记当前位置选定经纬度
- PopupOverlay
- 直接使用Geocoding API
简介
1、工作原理
利用无线电通讯网络或 GPS 等定位方式来确定出移动设备所在的位置
获取自己的位置——LocationManager
LocationManager 的基本用法
1、获取LocationManager
LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
2、选择一个位置提供器来确定设备当前的位置——GPS_PROVIDER、NETWORK_PROVIDER 和 PASSIVE_PROVIDER
优缺点:前两种使用的比较多,分别表示使用 GPS 定位和使用网络定位。这两种定位方式各有特点,GPS 定位的精准度比较高,但是非常耗电,而网络定位的精准度稍差,但耗电量比较少。GPS在室内几乎挂~
定位功能必须要由用户主动去启用才行,不然任何应用程序都无法获取到手机当前的位置信息。(定位服务中的使用无线网络和使用GPS卫星。)
3、将选择好的位置提供器传入到getLastKnownLocation()方法中,得到Location对象。
这个 Location 对象中包含了经度、纬度、海拔等一系列的位置信息
String provider = LocationManager.NETWORK_PROVIDER;
Location location = locationManager.getLastKnownLocation(provider);
4、如果想使用GPS位置提供器,确认GPS定位功能是都以及开启?先判断有哪些位置提供器可用。
List<String> providerList = locationManager.getProviders(true);
getProviders()方法接收一个布尔型参数,传入 true 就表示只有启用的位置提供器才会被返回。之后再从 providerList 中判断是否包含 GPS 定位的功能就行了。
5、实时更新位置信息——requestLocationUpdates()
LocationManager 还提供了一个 requestLocationUpdates()方法,只要传入一个 LocationListener 的实例,并简单配置几个参数就可以实现更新位置信息的获取了。
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, new LocationListener() {
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onLocationChanged(Location location) {
}
});
第一个参数是位置提供器的类型,第二个参数是监听位置变化的时间间隔,以毫秒为单位,第三个参数是监听位置变化的距离间隔,以米为单位,第四个参数则是 LocationListener 监听器。这样的话,LocationManager 每隔5 秒钟会检测一下位置的变化情况,当移动距离超过 10 米的时候,就会调用 LocationListener的 onLocationChanged()方法,并把新的位置信息作为参数传入。
实例
public class MainActivity extends Activity {
private TextView positionTextView;
private LocationManager locationManager;
private String provider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
positionTextView = (TextView) findViewById(R.id.position_text_view);
locationManager = (LocationManager) getSystemService(Context. LOCATION_SERVICE);
// 获取所有可用的位置提供器
List<String> providerList = locationManager.getProviders(true);
if (providerList.contains(LocationManager.GPS_PROVIDER)) {
provider = LocationManager.GPS_PROVIDER;
} else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {
provider = LocationManager.NETWORK_PROVIDER;
} else {
// 当没有可用的位置提供器时,弹出Toast提示用户
Toast.makeText(this, "No location provider to use",
Toast.LENGTH_SHORT).show();
return;
}
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
// 显示当前设备的位置信息
showLocation(location);
}
locationManager.requestLocationUpdates(provider, 5000, 1, locationListener);
}
protected void onDestroy() {
super.onDestroy();
if (locationManager != null) {
// 关闭程序时将监听器移除
locationManager.removeUpdates(locationListener);
}
}
LocationListener locationListener = new LocationListener() {
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onLocationChanged(Location location) {
// 更新当前设备的位置信息
showLocation(location);
}
};
private void showLocation(Location location) {
String currentPosition = "latitude is " + location.getLatitude() + "\n"
+ "longitude is " + location.getLongitude();
positionTextView.setText(currentPosition);
}
}
当程序关闭时,我们还需要调用 removeUpdates()方法来将位置监听器移除,以保证不会继续耗费手机的电量。
所需权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
反向地理编码,看懂位置信息(经纬度到具体位置)
直接使用Geocoding API
1、GeoCoder类
优点:可以非常简单地完成正向和反向的地理编码功能,从而轻松地将一个经纬值转换成看得懂的位置信息。
缺点:GeoCoder 长期存在着一些较为严重的 bug,在反向地理编码的时候会有一定的概率不能解析出位置的信息,这样就无法保证位置解析的稳定性
2、新的一套 Geocoding API
用法稍微复杂了一些,但稳定性要比 GeoCoder 强得多。
工作原理:
在手机端我们可以向谷歌的服务器发起一条 HTTP 请求,并将经纬度的值作为参数一同传递过去,服务器帮我们将这个经纬值转换成看得懂的位置信息,再将这些信息返回给手机端,最后手机端去解析服务器返回的信息,并进行处理就可以了。
查看官网:https://developers.google.com/maps/documentation/geocoding/start。
得到的数据再用解析方式解析出来。
实例
public class MainActivity extends Activity {
public static final int SHOW_LOCATION = 0;
......
private void showLocation(final Location location) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 组装反向地理编码的接口地址
StringBuilder url = new StringBuilder();
url.append("http://maps.googleapis.com/maps/api/geocode/json?latlng=");
url.append(location.getLatitude()).append(",")
url.append(location.getLongitude());
url.append("&sensor=false");
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url.toString());
// 在请求消息头中指定语言,保证服务器会返回中文数据
httpGet.addHeader("Accept-Language", "zh-CN");
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8");
JSONObject jsonObject = new JSONObject(response);
// 获取results节点下的位置信息
JSONArray resultArray = jsonObject.getJSONArray("results");
if (resultArray.length() > 0) {
JSONObject subObject = resultArray.
getJSONObject(0);
// 取出格式化后的位置信息
String address = subObject.getString("formatted_address");
Message message = new Message();
message.what = SHOW_LOCATION;
message.obj = address;
handler.sendMessage(message);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_LOCATION:
String currentPosition = (String) msg.obj;
positionTextView.setText(currentPosition);
break;
default:
break;
}
}
};
}
加上权限
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
使用百度地图
自己做过,毕竟谷歌的服务在中国支持还不是很给力,而且百度这个接口做的比较方便。
1、申请 API Key
2、创建应用
填写安全码:数字签名+;+包名
数字签名指的是我们打包程序时所用 keystore 的 SHA1指纹,可以在 Eclipse 中查看到。点击 Eclipse 导航栏的 Window→Preferences→Android→Build,
注意,目前我们使用的是 debug.keystore 所生成的指纹,这是 Android 自动生成的一个用于测试的keystore。而当你的应用程序发布时还需要创建一个正式的 keystore,如果要得到它的指纹,就需要在 cmd 中输入如下命令:
keytool -list -v –keystore
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
(4)显示地图
//百度SDK自定义控件,所以使用时候要加上类的全路径
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.baidu.mapapi.map.MapView
android:id="@+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" />
</LinearLayout>
public class MainActivity extends Activity {
private BMapManager manager;
private MapView mapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
manager = new BMapManager(this);
manager.init("SHVPoTtIpzfonPD3HCkc5sIt", null);
setContentView(R.layout.activity_main);
mapView = (MapView) findViewById(R.id.map_view);
mapView.setBuiltInZoomControls(true);
}
@Override
protected void onResume() {
mapView.onResume();
if (manager != null) {
manager.start();
}
super.onResume();
}
@Override
protected void onPause() {
mapView.onPause();
if (manager != null) {
manager.stop();
}
super.onPause();
}
@Override
protected void onDestroy() {
mapView.destroy();
if (manager != null) {
manager.destroy();
manager = null;
}
super.onDestroy();
}
}
代码分析:先需要创建一个 BMapManager 对象,然后调用它的 init()方法来进行初始化操作。init()方法接收两个参数,第一个参数就API Key , 第 二 个 参 数 传 入 null 即 可 。 注 意 初 始 化 操 作 一 定 要 在setContentView()方法前调用,不然的话就会出错。接下来我们获取到了 MapView 的实例,然后调用它的 setBuiltInZoomControls()方法并传入 true,表示启用内置的缩放控制功能。
另外还需要重写 onResume()、onPause()和 onDestroy()这三个方法,在这里对百度地图的 API 进行管理,以保证资源能够及时地得到释放。
(5)显示想要的位置而不是世界地图(比如自己所在位置)——MapController
//用 MapView的 getController()方法就能获取到 MapController 的实例。
//MapController地图的总控制器
MapController controller = mapView.getController();
//设置地图的缩放级别就可以这样写,取指范围3~19.级别越高,地图显示的信息越精细。
controller.setZoom(12);
(6)让地图定位到经纬度上——GeoPoint类
使用方式:GeoPoint 并没有什么太多的用法,主要就是用于存放经纬度值的,它的构造方法接收两个参数,第一个参数是纬度值,第二参数是经度值。但是需要注意, GeoPoint 是以微度为单位的,因此我们还要把经纬度的值乘以 10 的 6 次方再传给 GeoPoint。之后调用 MapController 的setCenter()方法,并把 GeoPoint 的实例传入就可以了
//将地图定位到北纬 39.915 度、东经 116.404 度这个位置
GeoPoint point = new GeoPoint((int) (39.915 * 1E6), (int) (116.404 * 1E6));
mMapController.setCenter(point);
实践
public class MainActivity extends Activity {
......
private LocationManager locationManager;
private String provider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
manager = new BMapManager(this);
manager.init("SHVPoTtIpzfonPD3HCkc5sIt", null);
setContentView(R.layout.activity_main);
mapView = (MapView) findViewById(R.id.map_view);
mapView.setBuiltInZoomControls(true);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// 获取所有可用的位置提供器
List<String> providerList = locationManager.getProviders(true);
if (providerList.contains(LocationManager.GPS_PROVIDER)) {
provider = LocationManager.GPS_PROVIDER;
} else if (providerList.contains(LocationManager.NETWORK_PROVIDER)) {
provider = LocationManager.NETWORK_PROVIDER;
} else {
// 当没有可用的位置提供器时,弹出Toast提示用户
Toast.makeText(this, "No location provider to use", Toast.LENGTH_SHORT).show();
return;
}
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
navigateTo(location);
}
}
private void navigateTo(Location location) {
MapController controller = mapView.getController();
controller.setZoom(16); // 设置缩放级别
GeoPoint point = new GeoPoint((int) (location.getLatitude() * 1E6), (int) (location.getLongitude() * 1E6));
controller.setCenter(point); // 设置地图中心点
}
......
}
使用覆盖物来增加更多功能
所有叠加或覆盖到地图上的内容都被统称为地图覆盖物,如标注、矢量图形元素、定位图标等。覆盖物拥有自己的地理坐标,当我们拖动或缩放地图时,它们会自动进行相应地移动。
常用覆盖物MyLocationOverlay——标记当前位置(选定经纬度)
1、作用:
在地图中添加一个图层,以标注出设备当前的位置
2、用法:
MyLocationOverlay myLocationOverlay = new MyLocationOverlay(mapView);
LocationData locationData = new LocationData();
// 指定我的位置
locationData.latitude = location.getLatitude();
locationData.longitude = location.getLongitude();
myLocationOverlay.setData(locationData);
mapView.getOverlays().add(myLocationOverlay);
mapView.refresh(); // 刷新使新增覆盖物生效
首先是创建了一个 MyLocationOverlay 的实例,然后通过 LocationData对 象 指 定 了 当 前 的 经 纬 度 数 据 , 并 调 用 setData() 方 法 将 LocationData 存 放 到 了MyLocationOverlay 中。之后通过 MapView 的 getOverlays()方法可以得到一个用于管理覆盖物的集合,再调用 add()方法将 MyLocationOverlay 这个覆盖物添加到集合中。最后,还需要调用一下 MapView 的 refresh()方法使新增的覆盖物生效。
PopupOverlay
允许我们自己指定覆盖物上显示的图片,并且还可以响应图片的点击事件,每个 PopupOverlay 上最多可以显示三张图片。
public class MainActivity extends Activity {
......
private void navigateTo(Location location) {
MapController controller = mapView.getController();
// 设置缩放级别
controller.setZoom(16);
GeoPoint point = new GeoPoint((int) (location.getLatitude() * 1E6), (int) (location.getLongitude() * 1E6));
// 设置地图中心点
controller.setCenter(point);
......
PopupOverlay pop = new PopupOverlay(mapView, new PopupClickListener() {
@Override
public void onClickedPopup(int index) {
// 相应图片的点击事件
Toast.makeText(MainActivity.this, "You clicked button " + index, Toast.LENGTH_SHORT).show();
}
});
// 创建一个长度为3的Bitmap数组
Bitmap[] bitmaps = new Bitmap[3];
try {
// 将三张图片读取到内存中
bitmaps[0] = BitmapFactory.decodeResource(getResources(), R.drawable.left);
bitmaps[1] = BitmapFactory.decodeResource(getResources(), R.drawable.middle);
bitmaps[2] = BitmapFactory.decodeResource(getResources(), R.drawable.right);
} catch (Exception e) {
e.printStackTrace();
}
pop.showPopup(bitmaps, point, 18);
}
......
}
showPopup()方法
接收三个参数,第一个参数就是前面创建的 Bitmap 数组,第二个参数是一个用于指定地理位置的 GeoPoint 对象,第三个参数是覆盖物在垂直方法上的偏移距离。