[置顶] 模仿百度地图的LBS服务——基础地图篇(v 3.1.1)



一、前言



转载请标明出处:http://blog.csdn.net/wlwlwlwl015/article/details/41076537

因为项目需要集成地图功能,所以交给我负责研究并继续完善百度地图的部分功能,之前完成了一部分,实现了将我们的数据以Poi的形式标记在地图上以及定位、方向传感器等功能(这部分是鸿洋大神在公司时写的代码 @鸿洋),后来根据需求要加入路线规划功能,即从我们当前的定位点到我们指定的Poi之间要怎么走,比如:通过公交、驾车或步行的方式,所以我就接手之前的代码进行修改和完善,下面就对我这几天学到的和做的东西做个总结和记录,对于新手而言,学习最重要。我的这个Demo是模仿百度地图的,由于模拟器的分辨率、网速和模拟器无法定位等问题,后面会贴出真机的效果图,下面的用模拟器截的动态效果图就凑活看一下哈。

本篇介绍的是基础地图相关的一些内容,路线规划在下一篇blog中记录(模仿百度地图的LBS服务——路线规划篇)。



二、项目背景



简单介绍一下我们的项目中需要集成的LBS部分。我们做的是一款旅游方面的APP,数据都是我们采集的特色数据,并以ListView的形式展示,当右滑时切换到地图模式,我们的数据就以Marker的形式展现在地图上,而且要提供路线规划(就是怎么去)的功能,下面是示意图:



点击周边特色功能,可以看到我们推荐的特色数据的ListView,而当进行右滑操作时切换到地图模式,即把我们ListView中的数据展示在地图上,并提供路线规划。下面就按顺序记录一下我这几天对于百度SDK路线规划的学习结果。



三、基础定位、Marker覆盖物、实时路况以及2D/3D的地图模式



这一部分的内容是百度地图SDK相对基本的东西,比较简单,官方的文档和Demo也很到位,之前鸿洋大神的博客也都介绍清楚了,我这里就针对我在使用过程中遇到的问题和觉得重要的地方做一些简单介绍。


1.基础定位


定位可以说是最基本的功能了,我们的项目中需要的是当前定位点到我们特色数据marker点的路径规划,所以定位自然是第一步,先看一下效果图:

[置顶] 模仿百度地图的LBS服务——基础地图篇(v 3.1.1)_第1张图片


可以看到进入程序之后自动定位到当前位置(模拟器无法定位,通过固定坐标模拟了定位的过程),下面贴上代码(整体地图相关功能都写在了一个Fragment中,由于代码过长,所以这里按顺序分段贴出)。

public class MyFragment extends Fragment implements
		OnGetRoutePlanResultListener, BaiduMap.OnMapClickListener,
		OnGetGeoCoderResultListener {

	// Map视图
	private MapView mMapView = null;
	// Map对象
	private BaiduMap mBaiduMap = null;
	// 定位客户端
	private LocationClient mLocationClient = null;
	// 当前定位模式
	private LocationMode mLocationMode = LocationMode.NORMAL;
	// 是否是第一次定位
	private volatile boolean isFristLocation = true;
        // 是否开始定位
        private boolean isStart = false;
	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		SDKInitializer.initialize(getActivity().getApplicationContext());
		View view = inflater.inflate(R.layout.activity_main, container, false);

		// Map视图
		mMapView = (MapView) view.findViewById(R.id.bmapView);
		// 初始化定位客户端
		mLocationClient = new LocationClient(getActivity()
				.getApplicationContext());
		return view;
	}

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onActivityCreated(savedInstanceState);
		initMapView();
		addListener();
	}

	@Override
	public void onStart() {
		// TODO Auto-generated method stub
		mBaiduMap.setMyLocationEnabled(true); // 获取是否允许定位图层
		if (!mLocationClient.isStarted()) {
			mLocationClient.start(); // 启动定位sdk
		}
		super.onStart();
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		mBaiduMap.setMyLocationEnabled(false);
		mLocationClient.stop(); // 关闭定位sdk
		mSearch.destroy();// 释放检索实例
		super.onDestroy();
	}

}

需要注意的是定位功能在onStart方法中开启,在onDestroy方法中关闭,这样做的目的是合理控制资源,定位功能不能一直开着,它是很费电的。当开启定位的时候就触发了定位监听器,而在定位之前还需要做一些初始化参数的设置,这部分代码应当写在定位之前,我这里是放在了initMapView()方法中:

private void initMapView() {
	// 初始化Map对象
	mBaiduMap = mMapView.getMap();
	// 设置地图模式(普通模式/卫星模式)
	mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);

	// 设置定位相关配置
	LocationClientOption option = new LocationClientOption();
	option.setCoorType("bd09ll");// 返回的定位结果是百度经纬度,默认值gcj02
	option.setOpenGps(true);// 打开gps
	option.setScanSpan(1000);// 设置发起定位请求的间隔时间为1000ms
	option.setIsNeedAddress(true);// 返回的定位结果包含地址信息
	mLocationClient.setLocOption(option);

	// 初始化覆盖物
	addMarkers();
}

定位监听器实现定位功能:

// 定位监听器
	class MyLocationListener implements BDLocationListener {
		@Override
		public void onReceiveLocation(BDLocation location) {
			// TODO Auto-generated method stub
			// map view 销毁后不在处理新接收的位置
			if (location == null || mMapView == null)
				return;
			isStart = true; // 是否开始定位
			mLocation = location; // 保存定位信息
			// 构造定位数据
			MyLocationData locData = new MyLocationData.Builder()
					.accuracy(location.getRadius())
					// 此处设置开发者获取到的方向信息,顺时针0-360
					.direction(100).latitude(location.getLatitude())
					.longitude(location.getLongitude()).build();
			// 设置定位数据
			mBaiduMap.setMyLocationData(locData);

			// 保存经纬度
			mCurrentAccracy = location.getRadius();
			mCurrentLantitude = location.getLatitude();
			mCurrentLongitude = location.getLongitude();

			// 设置定位图层的配置(定位模式,是否允许方向信息,用户自定义定位图标)
			BitmapDescriptor mCurrentMarker = BitmapDescriptorFactory
					.fromResource(R.drawable.navi_map_gps_locked);

			MyLocationConfiguration config = new MyLocationConfiguration(
					mLocationMode, true, mCurrentMarker);
			mBaiduMap.setMyLocationConfigeration(config);

			// 第一次定位时,将地图位置移动到当前位置
			if (isFristLocation) {
				isFristLocation = false;
				// 得到精度坐标信息
				LatLng ll = new LatLng(location.getLatitude(),
						location.getLongitude());
				LatLng ll1 = new LatLng(34.241743,
						108.969793);
				// 得到地图更新对象
				MapStatusUpdate u = MapStatusUpdateFactory.newLatLng(ll1);
				// 更新地图位置
				mBaiduMap.animateMapStatus(u);
			}
		}
	}

这个回调方法的参数保存了当前的定位信息,最后我给了一个34,108的坐标是为了让模拟器可以加载地图,正常情况下通过location.getXxx去获取就好了。最后两行代码是实现了地图的移动。


最后为定位客户端LocationClient注册定位监听器即可:

mLocationClient.registerLocationListener(new MyLocationListener());


2.Marker覆盖物


marker是Overlay的子类,在百度地图中覆盖物统称为Overlay,它的子类有以下几种:

Arc, Circle, Dot, GroundOverlay, Marker, Polygon, Polyline, Text

它们分别表示不同表现形式的覆盖物,比如:Text表示文字覆盖物等等。

我们用的是Marker覆盖物,由于BaiduMap直接提供了BaiduMap.OnMarkerClickListener,而其他覆盖物没有提供直接的点击事件,所以Marker的变成了我们覆盖物的首选。下面就贴出上面示例图片中添加红色大头针的代码:

	private void addMarkers() {
		List<LatLng> latlngs = new ArrayList<LatLng>();
		latlngs.add(new LatLng(34.24124, 109.013343)); 
		latlngs.add(new LatLng(34.24124, 109.013343));
		latlngs.add(new LatLng(34.27013, 108.938191)); 
		latlngs.add(new LatLng(34.234331, 108.992669)); 
		latlngs.add(new LatLng(34.266885, 108.98974)); 
		latlngs.add(new LatLng(34.241478, 108.969708)); 

		Marker marker = null;
		for (int i = 0; i < latlngs.size(); i++) {
			// 构建Marker图标
			BitmapDescriptor bitmap = BitmapDescriptorFactory
					.fromResource(R.drawable.ic_location_marka);
			// 构建MarkerOption,用于在地图上添加Marker
			OverlayOptions optionoo = new MarkerOptions().position(
					latlngs.get(i)).icon(bitmap);
			// 在地图上添加Marker,并显示
			marker = (Marker) mBaiduMap.addOverlay(optionoo);
			Bundle bundle = new Bundle();
			bundle.putCharSequence("info", "特色数据" + i);
			marker.setTitle("特色数据" + i);
			marker.setExtraInfo(bundle);
		}
	}

可以看到marker还可以存放一些数据,通过setExtraInfo(Bundle bundle)方法存值,那么在OnMarkerClickListener中的回调方法里就可以再通过marker.getExtraInfo()去获取值了。


现在再回到我们的需求,点击marker,弹出marker所在的地点信息并定位到当前marker,当点击marker上的“到这去”按钮,就应当弹出路线规划的选择框,然后绘制路线,下面暂且之放一个点击marker的效果图。由于点击左下角的定位图标的时候没有去模拟固定坐标,所以模拟器也无法获取我的位置信息了。

[置顶] 模仿百度地图的LBS服务——基础地图篇(v 3.1.1)_第2张图片


下面贴一下OnMarkerClickListener的代码:

	// 特色数据的覆盖物点击监听
	class MyMarkerClickListener implements OnMarkerClickListener {
		@Override
		public boolean onMarkerClick(Marker marker) {
			String title = marker.getTitle();
			// 判断title是否为null,特色数据的title不为null,路线节点的title为null
			if (title != null) {
				String info = marker.getExtraInfo().getCharSequence("info")
						.toString();
				// 定位到点击处
				LatLng position = marker.getPosition();
				// 更新当前marker的位置
				MyFragment.this.position = position;
				showPopupWindow(info, R.layout.popup2, R.id.tv_special_data,
						"暂时无法查询到数据");
				MapStatusUpdate u = MapStatusUpdateFactory.newLatLng(position);
				mBaiduMap.animateMapStatus(u);
				return true;
			} else {
				// Do nothing 相当于禁用marker
				return true;
			}

		}

这里还有一个值得一提的小技巧,由于绘制路线中的节点图标也是Marker,和我们自定义的Marker有冲突,所以应当禁用路线节点中的marker的点击事件,通过测试发现路线节点的marker的title是NULL,所以我们在添加覆盖物的时候随意set一个Title就能很好的区分出来,这样也间接实现了禁用路线中的marker的功能,关于路线节点这个东东到后面就知道了。


3.实时路况以及2D/3D/卫星地图模式


这个功能没有什么实质的作用,我这里是纯粹模仿百度地图做的,由于模拟器无法运行这个功能(一点就崩),下面一次贴出真机的效果图和代码,很简单。


首先是实时路况功能,不过根据官方文档看西安市好像还没有开实时路况,但是百度地图貌似也提供了,而且看起来还像那么回事,下班高峰看红线还挺多的(绿线便是畅通,红线表示堵塞)。下面是官方给出已开通实时路况的31个城市(南京,广州,重庆,东莞,长春,台州,福州,金华,北京,常州,杭州,温州,大连,南昌,宁波,沈阳,中山,珠海,佛山,泉州,石家庄,成都,青岛,深圳,武汉,乌鲁木齐,长沙,上海,天津,无锡,厦门)。

[置顶] 模仿百度地图的LBS服务——基础地图篇(v 3.1.1)_第3张图片


代码很简单,就是一个状态开关的开启与关闭而已。

	class MyIsOpenTraffeModeListenerTwo implements OnCheckedChangeListener {
		@Override
		public void onCheckedChanged(CompoundButton buttonView,
				boolean isChecked) {
			if (isChecked) {
				mBaiduMap.setTrafficEnabled(isChecked);
				buttonView.setBackground(getResources().getDrawable(
						R.drawable.btn_img_selector_3_click));
				Toast.makeText(getActivity(), "实时路况已打开", Toast.LENGTH_LONG)
						.show();
			} else {
				mBaiduMap.setTrafficEnabled(isChecked);
				buttonView.setBackground(getResources().getDrawable(
						R.drawable.btn_img_selector_3));
				Toast.makeText(getActivity(), "实时路况已关闭", Toast.LENGTH_LONG)
						.show();
			}
		}

	}


下面是3种地图模式,2D平面/3D模式以及卫星地图,其实2D和3D是二选一的模式,而卫星图模式应当和实时路况一样作为一个选项,但百度地图把这3个做成了一组单选结构,不知道为何这样设计,但我还是这样模仿了,并且修改了3D模式切会卫星模式效果消失的问题:

[置顶] 模仿百度地图的LBS服务——基础地图篇(v 3.1.1)_第4张图片[置顶] 模仿百度地图的LBS服务——基础地图篇(v 3.1.1)_第5张图片


代码也比较简单,3D图就是将地图的俯仰角设置45°即可。

	class MyRadioGroup4CheckMapModelListener implements
			android.widget.RadioGroup.OnCheckedChangeListener {
		@Override
		public void onCheckedChanged(RadioGroup group, int checkedId) {
			switch (checkedId) {
			case R.id.rb_statellite_mode:
				mBaiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
				break;
			case R.id.rb_normal_mode:
				float overlook = mBaiduMap.getMapStatus().overlook;
				if (overlook < 0) {
					MapStatus ms = new MapStatus.Builder(
							mBaiduMap.getMapStatus()).overlook(45).build();
					MapStatusUpdate u = MapStatusUpdateFactory.newMapStatus(ms);
					mBaiduMap.animateMapStatus(u);
				}
				mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
				break;
			case R.id.rb_three_d_mode:
				MapStatus ms = new MapStatus.Builder(mBaiduMap.getMapStatus())
						.overlook(-45).build();
				MapStatusUpdate u = MapStatusUpdateFactory.newMapStatus(ms);
				mBaiduMap.animateMapStatus(u);
			}
		}

	}



四、总结



本篇blog记录关于百度地图SDK的一些比较基本和简单的功能点,关于篇幅原因重点的路线规划部分将放到下一篇blog中记录(模仿百度地图的LBS服务——路线规划篇)。

你可能感兴趣的:(android,百度地图SDK,路线规划)