赞
踩
首先,我们需要去高德地图获取key,然后下载sdk(包括jar包和so文件),本项目需要的sdk,如图:
当我们将key下载好以后,并且将jar包和so文件放好——建议直接将jar包放在libs文件夹,然后将so文件放在jinLibs文件夹下面,当然,我们也可以将so文件也放到libs,然后将Module的build.gradle文件修改如图:
接下来就是修改文件清单,这些我们可以直接将API的代码复制过来了:
//地图包、搜索包、定位包、导航包需要的基础权限
<!--允许程序打开网络套接字-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序设置内置sd卡的写权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--允许程序访问WiFi网络信息-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--允许程序读写手机状态和身份-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--允许程序访问CellID或WiFi热点来获取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--用于访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--这个权限用于允许程序在手机屏幕关闭后后台进程仍然运行-->
<user-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:icon="@drawable/icon"
android:label="@string/app_name" >
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="e969e70f8daf003d370ab982e6ba656d"/>
……
<service android:name="com.amap.api.location.APSService"></service>
</application>
准备工作的最后一步只需要执行sync project with gradle files就完成整个项目的准备工作了!
准备工作完成了,我们接下来就执行第二步,展示地图:
首先,我们需要布局中有一个地图的布局,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="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">
<com.amap.api.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
接下来就是对于地图的调用,这个非常简单,API里面都是现成的,代码如下:
public class PiclocationActivity extends Activity {
private MapView mMapView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_piclocation);
ActionBar actionBar = getActionBar();
if(actionBar!=null)actionBar.hide();
mMapView = (MapView) findViewById(R.id.map);
mMapView.onCreate(savedInstanceState);// 此方法必须重写
}
@Override
protected void onDestroy() {
super.onDestroy();
//在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
mMapView.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
//在activity执行onResume时执行mMapView.onResume (),重新绘制加载地图
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//在activity执行onPause时执行mMapView.onPause (),暂停地图的绘制
mMapView.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState (outState),保存地图当前的状态
mMapView.onSaveInstanceState(outState);
}
}
于是地图就展示出来了。
当然,如果你是你现在看到的地图是没有中间的图标,位置也应该是北京为中心点的地图。而我们今天需要实现的是拾取坐标点,所以必须要显示当前定位的。而关于地图展示的相关方法,接下来还会用的到。所以,我们接下来就实现上面的效果吧!
其实,这个也代码量也非常简单,如下:
private AMap amap;
private OnLocationChangedListener mListener;
private AMapLocationClient mlocationClient;
private AMapLocationClientOption mLocationOption;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
initMap();
}
/**
* 地图实例化
*/
private void initMap() {
if (amap == null) {
amap = mMapView.getMap();
amap.setLocationSource(this);//设置了定位的监听,这里要实现LocationSource接口
amap.getUiSettings().setMyLocationButtonEnabled(true); // 是否显示定位按钮
amap.setMyLocationEnabled(true);//显示定位层并且可以触发定位,默认是flase
}
/**
* 激活定位
*/
@Override
public void activate(OnLocationChangedListener listener) {
mListener = listener;
if (mlocationClient == null) {
//初始化定位
mlocationClient = new AMapLocationClient(this);
//初始化定位参数
mLocationOption = new AMapLocationClientOption();
//设置定位回调监听
mlocationClient.setLocationListener(this);
//设置为高精度定位模式
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
//设置为定位一次
// mLocationOption.setOnceLocation(true);
//设置定位参数
mlocationClient.setLocationOption(mLocationOption);
// 此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗,
// 注意设置合适的定位时间的间隔(最小间隔支持为2000ms),并且在合适时间调用stopLocation()方法来取消定位请求
// 在定位结束后,在合适的生命周期调用onDestroy()方法
// 在单次定位情况下,定位无论成功与否,都无需调用stopLocation()方法移除请求,定位sdk内部会移除
mlocationClient.startLocation();//启动定位
}
}
/**
* 注销定位
*/
@Override
public void deactivate() {
mListener = null;
if (mlocationClient != null) {
mlocationClient.stopLocation();
mlocationClient.onDestroy();
}
mlocationClient = null;
}
/**
* 实现定位
* @param amapLocation
*/
@Override
public void onLocationChanged(AMapLocation amapLocation) {
if (mListener != null && amapLocation != null) {
if (amapLocation != null
&&amapLocation.getErrorCode() == 0) {
String location = "当前地点:"+amapLocation.getProvider()+amapLocation.getCity()+amapLocation.getStreet()+amapLocation.getStreetNum();
Log.d(TAG, "onLocationChanged: ");
} else {
String errText = "定位失败," + amapLocation.getErrorCode()+ ": " + amapLocation.getErrorInfo();
Log.e("AmapErr",errText);
}
}
}
接下来我们看看这个效果,首先看看日志:
04-29 23:53:04.267 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:06.268 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:08.270 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:10.277 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:12.327 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:14.281 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:16.282 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:18.288 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:20.297 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:22.291 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:24.640 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:26.295 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:28.301 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:30.298 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:32.303 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:34.304 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:36.352 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:38.339 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:40.340 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
04-29 23:53:42.341 18491-18491/cn.com.vicent.mymap D/PiclocationActivity: onLocationChanged: 当前地点:lbs成都市剑南大道南段712号
定位这么多次没有问题,但是地图并没有显示,why?
那我们现在将地图定位成功这个方法增加一句代码,实现地图显示当前位置:
mListener.onLocationChanged(amapLocation);// 显示系统小蓝点
其实上面还有一个问题,就是地图显示的缩放级别不一样,对吧?所以我们还需要在地图显示的时候预先设置缩放级别(程度).代码如下:
/**
* 地图实例化
*/
private void initMap() {
if (amap == null) {
...
amap.moveCamera(CameraUpdateFactory.zoomTo(15));//设置地图缩放级别
}
}
接下来我们看看效果:
这个效果已经基本成型了,但是我们还需要修改一下地图中心点的图标,我们不要圆形及边框,接下来看看代码:
/**
* 地图实例化
*/
private void initMap() {
if (amap == null) {
...
MyLocationStyle myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。
myLocationStyle.strokeColor(Color.TRANSPARENT);//设置定位蓝点精度圆圈的边框颜色
myLocationStyle.radiusFillColor(Color.TRANSPARENT);//设置定位蓝点精度圆圈的填充颜色
amap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style
}
}
OK,搞定!接下来,就要开始放大招了!我们需要点击屏幕的时候,显示当前位置以及周边的位置,实现高德地图这样的效果:
需要实现上述效果,这里需要实现几点功能:
于是我们接着上面的内容,开始进行第三步,周边搜索。
API里面介绍周边搜索叫做“获取POI数据”,并且有六种方式,如下:
因为我们需要的是周围的数据,所以这里我们选择第二种,这种情况比较合适。代码如下:
/**
* 周边数据实体封装
*/
private class ListViewHoldier{
String title;
String address;
LatLonPoint lp;
}
private List<ListViewHoldier> data = new ArrayList<>();
//第一个位置数据,设为成员变量是因为有多个地方需要使用
private ListViewHoldier lvHolder;
/**
* 地图实例化
*/
private void initMap() {
...
lvHolder = new ListViewHoldier();
...
}
/**
* 实现定位
* @param amapLocation
*/
@Override
public void onLocationChanged(AMapLocation amapLocation) {
if (mListener != null && amapLocation != null) {
if (amapLocation != null
&&amapLocation.getErrorCode() == 0) {
mListener.onLocationChanged(amapLocation);// 显示系统小蓝点
if(lvHolder.title==null){//只要第一次的数据,否则第一项的位置始终不会变。当然,也可以在这里关闭定位
// mlocationClient.stopLocation();//停止定位
lvHolder.title = "[位置]";
//关于定位数据,可以查看API http://lbs.amap.com/api/android-location-sdk/guide/android-location/getlocation
lvHolder.address = amapLocation.getProvider()+amapLocation.getCity()+amapLocation.getStreet()+amapLocation.getStreetNum();
lvHolder.lp = new LatLonPoint(amapLocation.getLatitude(),amapLocation.getLongitude());
data.add(0,lvHolder);
doSearchQuery();
}
} else {
String errText = "定位失败," + amapLocation.getErrorCode()+ ": " + amapLocation.getErrorInfo();
Log.e("AmapErr",errText);
}
}
}
/**
* 搜查周边数据
*/
private void doSearchQuery() {
//搜索类型
String type = "汽车服务|汽车销售|" +
"汽车维修|摩托车服务|餐饮服务|购物服务|生活服务|体育休闲服务|医疗保健服务|" +
"住宿服务|风景名胜|商务住宅|政府机构及社会团体|科教文化服务|交通设施服务|" +
"金融保险服务|公司企业|道路附属设施|地名地址信息|公共设施";
query = new PoiSearch.Query("", type, "");// 第一个参数表示搜索字符串,第二个参数表示poi搜索类型,第三个参数表示poi搜索区域(空字符串代表全国)
query.setPageSize(20);// 设置每页最多返回多少条poiitem
query.setPageNum(0);// 设置查第一页
poiSearch = new PoiSearch(this, query);
//搜索回调
poiSearch.setOnPoiSearchListener(this);
//搜索位置及范围
poiSearch.setBound(new PoiSearch.SearchBound(lvHolder.lp, 1000));
//同步搜索
// poiSearch.searchPOI();//不能在主线程实现耗时操作
//异步搜索
poiSearch.searchPOIAsyn();
}
/**
* 返回POI搜索异步处理的结果。
* @param result
* @param rcode
*/
@Override
public void onPoiSearched(PoiResult result, int rcode) {
if (rcode == AMapException.CODE_AMAP_SUCCESS) {
if (result != null && result.getQuery() != null) {// 搜索poi的结果
if (result.getQuery().equals(query)) {// 是否是同一条
// 取得搜索到的poiitems有多少页
List<PoiItem> poiItems = result.getPois();// 取得第一页的poiitem数据,页数从数字0开始
if (poiItems != null && poiItems.size() > 0) {
for (int i = 0;i<poiItems.size();i++){
PoiItem poiitem = poiItems.get(i);
ListViewHoldier holder = new ListViewHoldier();
holder.address = poiitem.getSnippet();
holder.title = poiitem.getTitle();
holder.lp = poiitem.getLatLonPoint();
if(data.size()>i+1){
data.remove(i+1);
}
data.add(i+1,holder);
Log.d(TAG, "onPoiSearched: \n"+holder.address+"\n"+holder.title+"\n"+holder.lp.toString());
}
} else {
}
}
} else {
}
} else {
}
}
/**
* poi id搜索的结果回调
* @param poiItem
* @param i
*/
@Override
public void onPoiItemSearched(PoiItem poiItem, int i) {
}
OK,代码比较长,我们接下来看看日志输出结果,检验一下我们的成果,日志如下:
04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段
小勇硅胶
30.528865,104.044311
04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段698号附11号
老草屋烧烤(华阳店)
30.528869,104.044336
04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
协和街道剑南大道南段712号
华宜·上锦
30.528781,104.044425
04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段
依爱思洗衣
30.528873,104.044673
04-30 12:12:39.010 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
协和街道剑南大道南段698号附8号
子墨西点
30.52902,104.04488
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段708-710号附近
乐诚家乐(剑南大道南段)
30.528738,104.044926
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
协和街道剑南大道南段706号
大邑血旺家常菜
30.528702,104.045092
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段708-710号附近
欣家超市
30.528637,104.04507
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段708-710
美家装饰建材(华阳店)
30.528752,104.045118
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段706号附近
贝尔康药业(同福堂大药房)
30.528835,104.045141
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段美家建材旁
牛肉面&馄饨王&黄焖鸡米饭(剑南大道店)
30.528964,104.045165
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
双流区
元景路与剑南大道南段交叉口
30.529386,104.045866
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
515路;521路;814路
剑南大道元景路口站(公交站)
30.529922,104.045593
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
天府新区华府大道一段996号(金茂)光明城市5栋4单元
菜鸟驿站(光明城市优超市周6寄件免费)
30.5273,104.04558
04-30 12:12:39.020 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
元景路与剑南大道南段交叉口东100米
停车场(元景路)
30.529275,104.046573
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
G123路;T207路
元景路剑南大道口站(公交站)
30.529392,104.046562
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
元景路金茂光明城市3号门12栋4单元2层
海鸣画室二号店
30.529139,104.046657
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
剑南大道南段
顺发超市
30.531082,104.044417
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
华阳镇剑南大道南段688号
骑龙小区
30.526689,104.043652
04-30 12:12:39.030 18831-18831/cn.com.vicent.mymap D/PiclocationActivity: onPoiSearched:
华阳华府大道一段996号
11摄影工作室
30.527685,104.046519
OK,周边搜索已经搞定,接下来执行下一步。
其实上面有一个bug,就是我们手指滑动是时候,然后定位成功就会将地图的中心点重新定位会我们当前的位置,如图:
原因是高德地图默认每秒更新一次当前位置,所以每秒地图就会回到中心点一次。解决方法很多,可以关闭定位,或者将定位成功后的显示系统小蓝点给关掉,代码如下:
mListener.onLocationChanged(amapLocation);// 显示系统小蓝点
现在我们的地图还只是可以随意滑动,但是我们需要在手指滑动结束的时候获取到经纬度,然后更新我们的周边数据。更新周边的数据已经封装好一个方法了,问题是在手指滑动的时候获取到经纬度。并且,在打开地图的时候中心点覆盖系统图标上面还有一个图标,并且该图标随手指移动,在手指结束滑动的时候还有一个动画。所以整理一下思路,接下来需要实现的有如下几点:
首先我们来实现对地图添加自定义图标,代码如下:
private Marker mEndMarker;
/**
* 实现定位
* @param amapLocation
*/
@Override
public void onLocationChanged(AMapLocation amapLocation) {
...
// 初始化Marker添加到地图
mEndMarker = amap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.end))));
mEndMarker.setPosition(new LatLng(amapLocation.getLatitude(),amapLocation.getLongitude()));
...
}
完成了第1步,接下来实现第2步,就是手指滑动的时候图标随手指滑动。这里我们需要实现一个地图状态发生变化的监听接口——AMap.OnCameraChangeListener,它有两个抽象方法,如下:
/**
* 地图实例化
*/
private void initMap() {
if (amap == null) {
...
amap.setOnCameraChangeListener(this);
}
}
/**
* 在地图状态改变过程中回调此方法。
* @param cameraPosition
*/
@Override
public void onCameraChange(CameraPosition cameraPosition) {
mEndMarker.setPosition(cameraPosition.target);
}
/**
* 在地图状态改变完成时回调此方法。
* @param cameraPosition
*/
@Override
public void onCameraChangeFinish(CameraPosition cameraPosition) {
}
接下来实现第3步,在滑动结束时启动一个动画。所以我们直接在onCameraChangeFinish(CameraPosition cameraPosition)方法里面执行动画的方法即可,代码如下:
/**
* 在地图状态改变完成时回调此方法。
* @param cameraPosition
*/
@Override
public void onCameraChangeFinish(CameraPosition cameraPosition) {
jumpPoint(mEndMarker);
}
/**
* marker点击时跳动一下
*/
public void jumpPoint(final Marker marker) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
//获取地图投影坐标转换器
Projection proj = amap.getProjection();
final LatLng markerLatlng = marker.getPosition();
Point markerPoint = proj.toScreenLocation(markerLatlng);
markerPoint.offset(0, -50);
final LatLng startLatLng = proj.fromScreenLocation(markerPoint);
final long duration = 500;
final Interpolator interpolator = new BounceInterpolator();
handler.post(new Runnable() {
@Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ duration);
double lng = t * markerLatlng.longitude + (1 - t)
* startLatLng.longitude;
double lat = t * markerLatlng.latitude + (1 - t)
* startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
handler.postDelayed(this, 16);
}
}
});
}
上面这些比较花哨的动作完成以后,我们得干一点实事,就是获取数据,并且及时更新。首先,我们需要在手指滑动结束的时候获取到经纬度,然后通过逆地理编码方法拿到地址的详细数据,再接着通过经纬度查询周边数据。所以这1步其实有三个动作。我们就不一步步的列了,直接上代码,看注释:
/**
* 逆地理编码业务类
*/
private GeocodeSearch geocoderSearch;
/**
* 地图实例化
*/
private void initMap() {
if (amap == null) {
...
//初始化 geocoderSearch
geocoderSearch = new GeocodeSearch(this);
//注册 逆地理编码异步处理回调接口
geocoderSearch.setOnGeocodeSearchListener(this);
}
}
/**
* 在地图状态改变完成时回调此方法。
* @param cameraPosition
*/
@Override
public void onCameraChangeFinish(CameraPosition cameraPosition) {
//当地图定位成功的时候该方法也会回调,为了避免不必要的搜索,因此此处增加一个判断
if(isFirstTime){
isFirstTime = false;
return;
}
//marker 动画
jumpPoint(mEndMarker);
lvHolder.lp = new LatLonPoint(cameraPosition.target.latitude,cameraPosition.target.longitude);
RegeocodeQuery query = new RegeocodeQuery(lvHolder.lp, 200,
GeocodeSearch.AMAP);// 第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系
geocoderSearch.getFromLocationAsyn(query);// 设置异步逆地理编码请求
doSearchQuery();
}
/**
* 逆地理编码回调方法
* 经纬度转位置
* @param result
* @param rCode
*/
@Override
public void onRegeocodeSearched(RegeocodeResult result, int rCode) {
if (rCode == AMapException.CODE_AMAP_SUCCESS) {
if (result != null && result.getRegeocodeAddress() != null
&& result.getRegeocodeAddress().getFormatAddress() != null) {
lvHolder.address = result.getRegeocodeAddress().getFormatAddress();
data.remove(0);
data.add(0,lvHolder);
} else {
// ToastUtil.show(ReGeocoderActivity.this, R.string.no_result);
}
} else {
// ToastUtil.showerror(this, rCode);
}
}
/**
* 地理编码回调方法
* 位置转经纬度
* @param geocodeResult
* @param i
*/
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int i) {
}
上面把主要内容都已经实现了,最后就是将数据封装好,放入ListView里面。因为后文还将用到RecycleView,所以这里就直接使用RecycleView,以便使用统一的Adapter。
修改代码,首先是布局,如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="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"
android:gravity="center">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:background="@color/gray">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/back"
android:layout_alignParentLeft="true"
android:layout_centerHorizontal="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_centerInParent="true"
android:text="地图选点"
android:textSize="16sp"/>
</RelativeLayout>
<com.amap.api.maps.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="5"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/ll_rl_locations"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:visibility="gone">
</android.support.v7.widget.RecyclerView>
<LinearLayout
android:id="@+id/ll_ll_holderview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:orientation="horizontal"
android:gravity="center">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/loading"/>
</LinearLayout>
</LinearLayout>
接下来是所有代码,直接看注释吧:
package cn.com.vicent.mymap;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.animation.BounceInterpolator;
import android.view.animation.Interpolator;
import android.widget.TextView;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.LocationSource;
import com.amap.api.maps.MapView;
import com.amap.api.maps.Projection;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.MyLocationStyle;
import com.amap.api.services.core.AMapException;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.core.PoiItem;
import com.amap.api.services.geocoder.GeocodeResult;
import com.amap.api.services.geocoder.GeocodeSearch;
import com.amap.api.services.geocoder.RegeocodeQuery;
import com.amap.api.services.geocoder.RegeocodeResult;
import com.amap.api.services.poisearch.PoiResult;
import com.amap.api.services.poisearch.PoiSearch;
import com.zhy.adapter.recyclerview.CommonAdapter;
import com.zhy.adapter.recyclerview.base.ViewHolder;
import java.util.ArrayList;
import java.util.List;
public class PiclocationActivity extends Activity implements LocationSource, AMapLocationListener, PoiSearch.OnPoiSearchListener, AMap.OnCameraChangeListener, GeocodeSearch.OnGeocodeSearchListener {
private static final String TAG = "PiclocationActivity";
private MapView mMapView = null;
private AMap amap;
private Marker mEndMarker;
private OnLocationChangedListener mListener;
private AMapLocationClient mlocationClient;
private AMapLocationClientOption mLocationOption;
/**
* 周边搜索条件
*/
private PoiSearch.Query query;
/**
* 周边搜索的业务执行
*/
private PoiSearch poiSearch;
/**
* 逆地理编码业务类
*/
private GeocodeSearch geocoderSearch;
/**
* 第一次定位的标志位
*/
private boolean isFirstTime = true;
private Context mContext;
private List<ListViewHoldier> data = new ArrayList<>();
//第一个位置数据,设为成员变量是因为有多个地方需要使用
private ListViewHoldier lvHolder;
private RecyclerView mRecyclerView;
private CommonAdapter mAdapter;
private View progressDialogView;
private TextView tvHint;
private View progressbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_piclocation);
ActionBar actionBar = getActionBar();
if(actionBar!=null)actionBar.hide();
initView();
mMapView.onCreate(savedInstanceState);// 此方法必须重写
initMap();
}
private void initView() {
mMapView = (MapView) findViewById(R.id.map);
mRecyclerView = (RecyclerView) findViewById(R.id.ll_rl_locations);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = getAdapter();
mRecyclerView.setAdapter(mAdapter);
progressDialogView = findViewById(R.id.ll_ll_holderview);
tvHint = (TextView) findViewById(R.id.ll_tv_hint);
progressbar = findViewById(R.id.ll_progressbar);
}
/**
* 地图实例化
*/
private void initMap() {
if (amap == null) {
amap = mMapView.getMap();
amap.setLocationSource(this);//设置了定位的监听,这里要实现LocationSource接口
amap.getUiSettings().setMyLocationButtonEnabled(true); // 是否显示定位按钮
amap.setMyLocationEnabled(true);//显示定位层并且可以触发定位,默认是flase
amap.moveCamera(CameraUpdateFactory.zoomTo(15));//设置地图缩放级别
MyLocationStyle myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。
myLocationStyle.strokeColor(Color.TRANSPARENT);//设置定位蓝点精度圆圈的边框颜色
myLocationStyle.radiusFillColor(Color.TRANSPARENT);//设置定位蓝点精度圆圈的填充颜色
amap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style
lvHolder = new ListViewHoldier();
//天添加屏幕移动的监听
amap.setOnCameraChangeListener(this);
// 初始化Marker添加到地图
mEndMarker = amap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.end))));
//初始化 geocoderSearch
geocoderSearch = new GeocodeSearch(this);
//注册 逆地理编码异步处理回调接口
geocoderSearch.setOnGeocodeSearchListener(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//在activity执行onDestroy时执行mMapView.onDestroy(),销毁地图
mMapView.onDestroy();
}
@Override
protected void onResume() {
super.onResume();
//在activity执行onResume时执行mMapView.onResume (),重新绘制加载地图
mMapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//在activity执行onPause时执行mMapView.onPause (),暂停地图的绘制
mMapView.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//在activity执行onSaveInstanceState时执行mMapView.onSaveInstanceState (outState),保存地图当前的状态
mMapView.onSaveInstanceState(outState);
}
/**
* 激活定位
* @param listener
*/
@Override
public void activate(OnLocationChangedListener listener) {
mListener = listener;
if (mlocationClient == null) {
//初始化定位
mlocationClient = new AMapLocationClient(this);
//初始化定位参数
mLocationOption = new AMapLocationClientOption();
//设置定位回调监听
mlocationClient.setLocationListener(this);
//设置为高精度定位模式
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
//设置为定位一次
// mLocationOption.setOnceLocation(true);
//设置定位参数
mlocationClient.setLocationOption(mLocationOption);
// 此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗,
// 注意设置合适的定位时间的间隔(最小间隔支持为2000ms),并且在合适时间调用stopLocation()方法来取消定位请求
// 在定位结束后,在合适的生命周期调用onDestroy()方法
// 在单次定位情况下,定位无论成功与否,都无需调用stopLocation()方法移除请求,定位sdk内部会移除
mlocationClient.startLocation();//启动定位
}
}
/**
* 注销定位
*/
@Override
public void deactivate() {
mListener = null;
if (mlocationClient != null) {
mlocationClient.stopLocation();
mlocationClient.onDestroy();
}
mlocationClient = null;
}
/**
* 实现定位
* @param amapLocation
*/
@Override
public void onLocationChanged(AMapLocation amapLocation) {
if (mListener != null && amapLocation != null) {
if (amapLocation != null
&&amapLocation.getErrorCode() == 0) {
if(isFirstTime){//只要第一次的数据,当然,也可以在这里关闭定位
// mlocationClient.stopLocation();//停止定位
mListener.onLocationChanged(amapLocation);// 显示系统小蓝点
lvHolder.title = "[位置]";
lvHolder.address = amapLocation.getProvider()+amapLocation.getCity()+amapLocation.getStreet()+amapLocation.getStreetNum();
lvHolder.lp = new LatLonPoint(amapLocation.getLatitude(),amapLocation.getLongitude());
mEndMarker.setPosition(new LatLng(amapLocation.getLatitude(),amapLocation.getLongitude()));
data.add(0,lvHolder);
doSearchQuery();
}
} else {
String errText = "定位失败," + amapLocation.getErrorCode()+ ": " + amapLocation.getErrorInfo();
Log.e("AmapErr",errText);
}
}
}
/**
* 搜查周边数据
*/
private void doSearchQuery() {
//搜索类型
String type = "汽车服务|汽车销售|" +
"汽车维修|摩托车服务|餐饮服务|购物服务|生活服务|体育休闲服务|医疗保健服务|" +
"住宿服务|风景名胜|商务住宅|政府机构及社会团体|科教文化服务|交通设施服务|" +
"金融保险服务|公司企业|道路附属设施|地名地址信息|公共设施";
query = new PoiSearch.Query("", type, "");// 第一个参数表示搜索字符串,第二个参数表示poi搜索类型,第三个参数表示poi搜索区域(空字符串代表全国)
query.setPageSize(20);// 设置每页最多返回多少条poiitem
query.setPageNum(0);// 设置查第一页
poiSearch = new PoiSearch(this, query);
//搜索回调
poiSearch.setOnPoiSearchListener(this);
//搜索位置及范围
poiSearch.setBound(new PoiSearch.SearchBound(lvHolder.lp, 1000));
//同步搜索
// poiSearch.searchPOI();//不能在主线程实现耗时操作
//异步搜索
poiSearch.searchPOIAsyn();
}
/**
* 返回POI搜索异步处理的结果。
* @param result
* @param rcode
*/
@Override
public void onPoiSearched(PoiResult result, int rcode) {
if (rcode == AMapException.CODE_AMAP_SUCCESS) {
if (result != null && result.getQuery() != null) {// 搜索poi的结果
if (result.getQuery().equals(query)) {// 是否是同一条
// 取得搜索到的poiitems有多少页
List<PoiItem> poiItems = result.getPois();// 取得第一页的poiitem数据,页数从数字0开始
if (poiItems != null && poiItems.size() > 0) {
for (int i = 0;i<poiItems.size();i++){
PoiItem poiitem = poiItems.get(i);
ListViewHoldier holder = new ListViewHoldier();
holder.address = poiitem.getSnippet();
holder.title = poiitem.getTitle();
holder.lp = poiitem.getLatLonPoint();
if(data.size()>i+1){
data.remove(i+1);
}
data.add(i+1,holder);
}
mAdapter.notifyDataSetChanged();
mRecyclerView.setVisibility(View.VISIBLE);
progressDialogView.setVisibility(View.GONE);
} else {
progressbar.setVisibility(View.GONE);
tvHint.setText(R.string.no_location);
}
}
} else {
progressbar.setVisibility(View.GONE);
tvHint.setText(R.string.no_location);
}
} else {
progressbar.setVisibility(View.GONE);
tvHint.setText(R.string.no_location);
}
}
/**
* poi id搜索的结果回调
* @param poiItem
* @param i
*/
@Override
public void onPoiItemSearched(PoiItem poiItem, int i) {
}
/**
* 在地图状态改变过程中回调此方法。
* @param cameraPosition
*/
@Override
public void onCameraChange(CameraPosition cameraPosition) {
mEndMarker.setPosition(cameraPosition.target);
}
/**
* 在地图状态改变完成时回调此方法。
* @param cameraPosition
*/
@Override
public void onCameraChangeFinish(CameraPosition cameraPosition) {
//当地图定位成功的时候该方法也会回调,为了避免不必要的搜索,因此此处增加一个判断
if(isFirstTime){
isFirstTime = false;
return;
}
//隐藏数据
mRecyclerView.setVisibility(View.GONE);
//展示dialogView
progressDialogView.setVisibility(View.VISIBLE);
findViewById(R.id.ll_progressbar).setVisibility(View.VISIBLE);
tvHint.setText(R.string.loading);
//marker 动画
jumpPoint(mEndMarker);
lvHolder.lp = new LatLonPoint(cameraPosition.target.latitude,cameraPosition.target.longitude);
RegeocodeQuery query = new RegeocodeQuery(lvHolder.lp, 200,
GeocodeSearch.AMAP);// 第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系
geocoderSearch.getFromLocationAsyn(query);// 设置异步逆地理编码请求
doSearchQuery();
}
/**
* marker点击时跳动一下
*/
public void jumpPoint(final Marker marker) {
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
//获取地图投影坐标转换器
Projection proj = amap.getProjection();
final LatLng markerLatlng = marker.getPosition();
Point markerPoint = proj.toScreenLocation(markerLatlng);
markerPoint.offset(0, -50);
final LatLng startLatLng = proj.fromScreenLocation(markerPoint);
final long duration = 500;
final Interpolator interpolator = new BounceInterpolator();
handler.post(new Runnable() {
@Override
public void run() {
long elapsed = SystemClock.uptimeMillis() - start;
float t = interpolator.getInterpolation((float) elapsed
/ duration);
double lng = t * markerLatlng.longitude + (1 - t)
* startLatLng.longitude;
double lat = t * markerLatlng.latitude + (1 - t)
* startLatLng.latitude;
marker.setPosition(new LatLng(lat, lng));
if (t < 1.0) {
handler.postDelayed(this, 16);
}
}
});
}
/**
* 逆地理编码回调方法
* 经纬度转位置
* @param result
* @param rCode
*/
@Override
public void onRegeocodeSearched(RegeocodeResult result, int rCode) {
if (rCode == AMapException.CODE_AMAP_SUCCESS) {
if (result != null && result.getRegeocodeAddress() != null
&& result.getRegeocodeAddress().getFormatAddress() != null) {
lvHolder.address = result.getRegeocodeAddress().getFormatAddress();
data.remove(0);
data.add(0,lvHolder);
} else {
// ToastUtil.show(ReGeocoderActivity.this, R.string.no_result);
}
} else {
// ToastUtil.showerror(this, rCode);
}
}
/**
* 地理编码回调方法
* 位置转经纬度
* @param geocodeResult
* @param i
*/
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int i) {
}
private CommonAdapter getAdapter() {
return new CommonAdapter<ListViewHoldier>(this,R.layout.item_listview_location,data) {
@Override
protected void convert(ViewHolder holder, final ListViewHoldier listViewHoldier, int position) {
holder.setText(R.id.rl_tv_name,listViewHoldier.title);
holder.setText(R.id.rl_tv_location,listViewHoldier.address);
holder.getView(R.id.rl_tv_subit).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(mContext,NavigationActivity.class);
intent.putExtra("value",listViewHoldier.lp);
intent.putExtra("address",listViewHoldier.address);
setResult(RESULT_OK,intent);
finish();
}
});
}
};
}
/**
* 周边数据实体封装
*/
private class ListViewHoldier{
String title;
String address;
LatLonPoint lp;
}
}
最后我们来看看效果怎么样?
由于对地图相关API不熟悉,走了很多弯路。最后还有一个bug,就是加载地图的时候有时候不会显示到我们设置的缩放级别,还有就是有时候打开地图的时候,会看不见marker,一般都是时间或者网络的原因,大家可以测试一下。最后,如有错误,希望大家多多指正,一起进步,谢谢!
相关API:
http://lbs.amap.com/api/android-sdk/summary/
http://a.amap.com/lbs/static/unzip/Android_Map_Doc/index.html
http://lbs.amap.com/api/android-location-sdk/locationsummary/
http://amappc.cn-hangzhou.oss-pub.aliyun-inc.com/lbs/static/unzip/Android_Location_Doc/index.html
http://lbs.amap.com/api/android-navi-sdk/summary/
http://a.amap.com/lbs/static/unzip/Android_Navi_Doc/index.html
参考文章:
http://blog.csdn.net/u010378579/article/details/53907749
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。