赞
踩
基于位置的服务(Location Based Service )简称LBS,这个技术随着移动互联网的兴起,在最近的几年里十分火爆。其实它本身并不是什么时髦的技术,主要的工作原理就是利用无线电通讯网络或GPS等定位方式来确定出移动设备所在的位置,而这种定位技术早在很多年前就已经出现了。
那为什么LBS技术直到最近几年才开始流行呢?这主要是因为,在过去移动设备的功能极其有限,即使定位到了设备所在的位置,也就仅仅只是定位到了而已,我们并不能在位置的基础上进行一些其他的操作。而现在就大大不同了,有了Android系统作为载体,我们可以利用定位出的位置进行许多丰富多彩的操作。
先看一个例子:
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txt_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
启动类:
package com.test.location; import android.app.Activity; import android.content.Context; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast; import com.test.R; import java.util.List; /** * Created by Administrator on 2016/5/15 0015. */ public class LocationActivity extends Activity { private TextView txtLocation; private LocationManager locationManager; private String provider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_location); txtLocation = (TextView) findViewById(R.id.txt_location); //调用Context的getSystemService()方法获取到LocationManager的实例。 // getSystemService()方法接收一个字符串参数用于确定获取系统的哪个服务,这里传入Context.LOCATION_SERVICE即可。 locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); //获取所有可用的位置提供器来确定设备当前的位置 //如果有些时候你想让定位的精度尽量高一些,但又不确定GPS定位的功能是否已经启用, //这个时候就可以先判断一下有哪些位置提供器可用 //getProviders()方法接收一个布尔型参数,传入true就表示只有启用的位置提供器才会被返回。 //之后再从providerList中判断是否包含GPS定位的功能就行了。 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_LONG); return; } //将选择好的位置提供器传入到getLastKnownLocation()方法中,就可以得到一个Location对象 //这个Location对象中包含了经度、纬度、海拔等一系列的位置信息,然后从中取出我们所关心的那部分数据即可。 Location location = locationManager.getLastKnownLocation(provider); if (location != null) { showLocation(location); } //在设备位置发生改变的时候获取到最新的位置信息用这个方法requestLocationUpdates(),该方法接收四个参数, // 第一个参数是位置提供器的类型, // 第二个参数是监听位置变化的时间间隔,以毫秒为单位, // 第三个参数是监听位置变化的距离间隔,以米为单位, // 第四个参数则是LocationListener监听器。 locationManager.requestLocationUpdates(provider, 5000, 1, locationListener); } LocationListener locationListener = new LocationListener() { @Override public void onLocationChanged(Location location) { //更新当前设备位置 showLocation(location); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public void onProviderEnabled(String provider) { } @Override public void onProviderDisabled(String provider) { } }; @Override protected void onDestroy() { super.onDestroy(); if (locationManager != null) { //关闭程序时将监听器移除 locationManager.removeUpdates(locationListener); } } private void showLocation(Location location) { String currentPosition = "latitude is " + location.getLatitude() + "\n" + "longitude is " + location.getLongitude(); txtLocation.setText(currentPosition); } }
声明权限:
<!--获得当前设备位置的权限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Android中一般有三种位置提供器可供选择,GPS_PROVIDER、NETWORK_PROVIDER和PASSIVE_PROVIDER。其中前两种使用的比较多,分别表示使用GPS定位和使用网络定位。这两种定位方式各有特点,GPS定位的精准度比较高,但是非常耗电,而网络定位的精准度稍差,但耗电量比较少。我们应该根据自己的实际情况来选择使用哪一种位置提供器,当位置精度要求非常高的时候,最好使用GPS_PROVIDER,而一般情况下,使用NETWORK_PROVIDER会更加得划算。
需要注意的是,定位功能必须要由用户主动去启用才行,不然任何应用程序都无法获取到手机当前的位置信息。进入手机的设置→定位服务,其中第一个选项表示允许使用网络的方式来对手机进行定位,第二个选项表示允许使用GPS的方式来对手机进行定位。
其实Android本身就提供了地理编码的API,主要是使用GeoCoder这个类来实现的。它可以非常简单地完成正向和反向的地理编码功能,从而轻松地将一个经纬值转换成看得懂的位置信息。不过,非常遗憾的是,GeoCoder长期存在着一些较为严重的bug,在反向地理编码的时候会有一定的概率不能解析出位置的信息,这样就无法保证位置解析的稳定性,因此我们不得不去寻找GeoCoder的替代方案。还算比较幸运,谷歌又提供了一套Geocoding API,使用它的话也可以完成反向地理编码的工作,只不过它的用法稍微复杂了一些,但稳定性要比GeoCoder强得多。
Geocoding API的工作原理并不神秘,其实就是利用了我们上一章中学习的HTTP协议。在手机端我们可以向谷歌的服务器发起一条HTTP请求,并将经纬度的值作为参数一同传递过去,然后服务器会帮我们将这个经纬值转换成看得懂的位置信息,再将这些信息返回给手机端,最后手机端去解析服务器返回的信息,并进行处理就可以了。
Geocoding API中规定了很多接口,其中反向地理编码的接口如下:
http://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&sensor=true_or_false
我们来仔细看下这个接口的定义,其中http://maps.googleapis.com/maps/api/geocode/是固定的,表示接口的连接地址。json表示希望服务器能够返回JSON格式的数据,这里也可以指定成xml。latlng=40.714224,-73.96145表示传递给服务器去解码的经纬值是北纬40.714224度,西经73.96145度。sensor=true_or_false表示这条请求是否来自于某个设备的位置传感器,通常指定成false即可。
如果发送http://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.96145&sensor=false这样一条请求给服务器,我们将会得到一段非常长的JSON格式的数据,其中会包括如下部分内容:“formatted_address” : “277 Bedford Avenue, 布鲁克林纽约州 11211美国”
从这段内容中我们就可以看出北纬40.714224度,西经73.96145度对应的地理位置是在哪里了。如果你想查看服务器返回的完整数据,在浏览器中访问上面的网址即可。
~
先申请成为百度开发者,然后创建应用,
安全码是我们申请API Key所必须填写的一个字段,它的组成方式是数字签名+;+包名。
这里数字签名指的是我们打包程序时所用keystore的SHA1指纹。
这边要用输入SHA1,在eclipse中可以通过点击Eclipse导航栏的Window→Preferences→Android→Build获得,而android studio的童鞋可以通过下面方法获得SHA1
方法:
第1步:运行进入控制台
第2步:定位到.android文件夹下,输入cd .android
第3步:输入keytool -list -v -keystore debug.keystore,会得到三种指纹证书,选取SHA1类型的证书(密钥口令是android),
其中keytool为jdk自带工具;keystorefile为Android 签名证书文件
下面是一个例子:
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.test.MainActivity">
<com.baidu.mapapi.map.MapView
android:id="@+id/my_map_view"
android:layout_width="match_parent"
android:clickable="true"
android:layout_height="match_parent" />
</RelativeLayout>
启动类:
package com.test; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.location.Location; import android.location.LocationManager; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.widget.Toast; import com.baidu.mapapi.BMapManager; import com.baidu.mapapi.map.LocationData; import com.baidu.mapapi.map.MapController; import com.baidu.mapapi.map.MapView; import com.baidu.mapapi.map.MyLocationOverlay; import com.baidu.mapapi.map.PopupClickListener; import com.baidu.mapapi.map.PopupOverlay; import com.baidu.platform.comapi.basestruct.GeoPoint; import java.util.List; public class MainActivity extends ActionBarActivity { private BMapManager manager; private MapView mapView; private LocationManager locationManager; private String provider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //创建一个BMapManager对象 manager = new BMapManager(this); //初始化,init()方法接收两个参数, // 第一个参数就是在上一小节中我们申请到的API Key, // 第二个参数传入null即可。 //注意初始化操作一定要在setContentView()方法前调用,不然的话就会出错。 manager.init("MKTYkwNGFlmnzhn9jL7DGDp5KFYHGZYC", null); setContentView(R.layout.activity_main); mapView = (MapView) this.findViewById(R.id.my_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(MainActivity.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对象(地图的总控制器),MapController类主要是对地图的放大缩小, //俯视旋转,手势键盘处理,指南针位置,动画等的管理 MapController cotroller = mapView.getController(); //设置缩放级别,取值范围是3到19,级别越高,地图显示的信息就越精细。 cotroller.setZoom(16); //GeoPoint主要就是用于存放经纬度值的,它的构造方法接收两个参数,第一个参数是纬度值,第二参数是经度值。 //但是需要注意,GeoPoint是以微度为单位的,因此我们还要把经纬度的值乘以10的6次方再传给GeoPoint。 //通过纬度、经度创建一个地理坐标点 GeoPoint point = new GeoPoint((int) (location.getLatitude() * 1E6), (int) (location.getLongitude() * 1E6)); //设置地图中心 cotroller.setCenter(point); //MyLocationOverlay类用于在地图上标注自己所处的位置 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(); PopupOverlay popup = new PopupOverlay(mapView, new PopupClickListener() { @Override public void onClickedPopup(int i) { //相应图片的点击事件 Toast.makeText(MainActivity.this, "You clicked button " + i, Toast.LENGTH_SHORT).show(); } }); //创建一个长度为3的Bitmap数组 Bitmap[] bitmaps = new Bitmap[3]; //将三张图片读取到内存中 bitmaps[0] = BitmapFactory.decodeResource(getResources(), R.drawable.left); bitmaps[1] = BitmapFactory.decodeResource(getResources(), R.drawable.middle); bitmaps[2] = BitmapFactory.decodeResource(getResources(), R.drawable.right); popup.showPopup(bitmaps, point, 18); } //重写onResume()、onPause()和onDestroy()这三个方法对百度地图的API进行管理,以保证资源能够及时地得到释放。 @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(); } super.onDestroy(); } }
声明权限:
<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" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
除了普通的地图展示之外,百度地图还提供了一种叫作覆盖物的功能,所有叠加或覆盖到地图上的内容都被统称为地图覆盖物,如标注、矢量图形元素、定位图标等。覆盖物拥有自己的地理坐标,当我们拖动或缩放地图时,它们会自动进行相应地移动。百度地图提供了很多种类型的覆盖物,开发人员可以根据自己的实际需求来选择使用哪些覆盖物,在百度地图所有的覆盖物中,最常用的就是 了,它主要的作用是可以在地图中添加一个图层,以标注出设备当前的位置。
MyLocationOverlay:
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(); // 刷新使新增覆盖物生效
下面我们再学习一下PopupOverlay这种覆盖物的用法吧。相比于 MyLocationOverlay,PopupOverlay 允许我们自己指定覆盖物上显示的图片,并且还可以响应图片的点击事件,每个 PopupOverlay上最多可以显示三张图片。
PopupOverlay:
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); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。