浅谈百度地图的简单开发之实现地图全景,内景展示功能(四)
百度地图,Android2016-05-27
今天,我就接着来浅谈一下关于百度地图的内景,外景的展示功能。今天具体要实现的功能就是输入该地点的名称然后就展示该地点的内景图片,有内景就展示内景,没有则显示该地点的街景,该功能是参考百度地图官方的API来实现。
实现街景展示的地点的搜索,需要涉及到几个方面的知识:HttpClient网络请求封装,解析JSON数据,封装解析后JSON数据。其实展示内景或者外景很简单,只需要获取到内景的uid(POI的id),外景的经纬度即可,然后通过相应的API即可设置。关键点就是如何去获得我输入地点的uid或者经纬度呢??这时候百度地图开放平台提供一个接口,就是输入一地点它就会返回该地点的uid和经纬度等信息的JSON数据,而我们只需要去解析这些JSON数据,就可以得到我们想要的uid和经纬度信息。大致的思路是这样的。
一、熟悉百度地图官方接口API数据的使用。
1、首先进入百度地图API官网,点击进入一个web服务API,然后选择PlaceAPI
通过以上介绍你应该就可以得到一个可以测试的URL,只要传入我们申请的APPKEY,根据传入不同的地点名称就会返回一个JSON数据。到底可不可以呢,其实我们可以测试一下,如果可以的话,我只要按要求拼接好URL,并把该URL粘贴到浏览器地址栏会返回一大串的JSON数据,如果返回了,那么我们成功拿到了数据,就可以进行下一步了。
http://api.map.baidu.com/place/v2/search?ak=3QIYhSeKPK770bpwzepo9GI1&output=json&query=北京大学&page_size=10&page_num=0&scope=1®ion=全国
看来我们这样的拼接的URL还是存在着问题,它说少了一个参数,其实仔细思考一下,发现也很正常,因为我们的APPKEY是申请的类型Android手机类型,而这里的接口是提供web服务的,如果申请的APPKEY的类型是JavaScript类型的话,那么就无需mcode参数。而如果是android或者IOS则需要在URL后面再增添一个mcode参数,那么我们知道mcode是必需的,那么这mcode参数值是什么呢??其实的它的值就是你的Eclipse中的SHA1 fingerprint值和你的项目的包名组合中间用“;"连接具体如下:
最终得到如下的URL:
http://api.map.baidu.com/place/v2/search?ak=nylibTfKmZw9mGe3juhT3U9x&output=json&query=北京大学&page_size=10&page_num=0&scope=1®ion=全国&mcode=F5:3E:06:3E:FC:E8:ED:19:60:2E:99:63:D8:78:85:2E:EB:12:9D:BE;com.mikyou.maptest
可以使用如下的URL粘贴浏览器测试一下,看是否可以返回JSON数据。最终发现可以返回JSON数据,那么到这里我们就知道了如何从百度官方拿到我们想要的数据。
并且从返回的JSON数据可以得到lat(经度),lng(纬度),streetId(街景id),uid(内景Id)等一系列的数据。那么接下来我们只需要利用的android的HttpClient中的get或post请求网络数据,得到返回的JSON数据,然后我们解析这些JSON数据得到经纬度,streetid,uid,然后把这些设置给百度全景展示的API即能实现全景和内景功能。
二、要实现全景功能需要在AndroidManifest.xml中配置一下BaseAppcation,用于对全景显示的初始化
package com.mikyou.tools; import android.app.Application; import android.content.Context; import android.widget.Toast; import com.baidu.lbsapi.BMapManager; import com.baidu.lbsapi.MKGeneralListener; import com.baidu.mapapi.SDKInitializer; public class BaseApplication extends Application { private static Context mContext; public static float sScale; public static int sWidthDp; public static int sWidthPix; public BMapManager mBMapManager = null; private static BaseApplication mInstance; public static BaseApplication getInstance() { return mInstance; } @Override public void onCreate() { super.onCreate(); SDKInitializer.initialize(this); initEngineManager(this); mContext = this; mInstance = this; sScale = getResources().getDisplayMetrics().density; sWidthPix = getResources().getDisplayMetrics().widthPixels; sWidthDp = (int) (sWidthPix / sScale); } public void initEngineManager(Context context) { if (mBMapManager == null) { mBMapManager = new BMapManager(context); } if (!mBMapManager.init(new MyGeneralListener())) { Toast.makeText( BaseApplication.getInstance().getApplicationContext(), "BMapManager!", Toast.LENGTH_LONG).show(); } } public static class MyGeneralListener implements MKGeneralListener { @Override public void onGetPermissionState(int iError) { if (iError != 0) { } else { } } } }
<application android:name="com.mikyou.tools.BaseApplication"<!-- 配置在此处 --> android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/AppTheme" > <meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="nylibTfKmZw9mGe3juhT3U9x" /> <activity android:name="com.mikyou.maptest.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" > <intent-filter> <action android:name="com.baidu.location.service_v2.2" > </action> </intent-filter> </service> <activity android:name="com.mikyou.maptest.OtherPnoramaActivity" android:label="@string/title_activity_other_pnorama" > </activity> </application>
三、实现HttpClient中的网络数据请求,自己个人封装一个HttpUtil类,里面有个方法它能直接返回一个JSON对象。
package com.mikyou.tools; import java.io.IOException; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.ParseException; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.json.JSONException; import org.json.JSONObject; /** * @author mikyou * 封装get和post方式请求 * HttpClient 维护session * 一个HttpClient对象代表一个浏览器 * 高度封装了客户端与服务端交互的,可以直接返回一个JSONObject对象 * */ public class HttpUtils { private static JSONObject jsonObjects; private static String entityString; private static HttpResponse res=null; private static String APPKEY="nylibTfKmZw9mGe3juhT3U9x";//APPKEY private static String outputForm="json";//返回网络数据的格式 private static String Head="http://api.map.baidu.com/place/v2/search?ak=";//URL接口地址首部 private static String IP=null; private static int pageSize=20;//一次返回数据总条数 private static String mcode="F5:3E:06:3E:FC:E8:ED:19:60:2E:99:63:D8:78:85:2E:EB:12:9D:BE;com.mikyou.maptest";//mcode参数 private static HttpClient clients=new DefaultHttpClient(); @SuppressWarnings("unused") public static JSONObject send(String destination,List<NameValuePair> params) throws ClientProtocolException, IOException, JSONException{ if (params==null) {//表示发送get方式请求,否则就发送Post发送请求(因为get方式不需要params) IP=Head+APPKEY+"&output="+outputForm+"&query="+destination+"&page_size="+pageSize+"&page_num=0&scope=1®ion=全国&mcode="+mcode; System.out.println("IP_Address:--------->"+IP); HttpGet get=new HttpGet(IP); res=clients.execute(get); }else{ HttpPost post=new HttpPost(IP); post.setEntity(new UrlEncodedFormEntity(params,HTTP.UTF_8)); res=clients.execute(post); } if (res.getStatusLine().getStatusCode()==200) { HttpEntity entity=res.getEntity(); entityString=EntityUtils.toString(entity,HTTP.UTF_8); System.out.println("httpUtils--------------->"+entityString); jsonObjects=new JSONObject(entityString); } return jsonObjects; } public static String getEntityString() { return entityString; } public static void setEntityString(String entityString) { HttpUtils.entityString = entityString; } }
接着就是使用HttpUtil去进行Http请求,我们在进行网络请求数据,一般都是耗时的操作,所以需要去开启一个子线程来专门进行网络请求,从而可以避免阻塞主线程不至于真个应用直接崩溃。子线程得到JSON对象后,还得需要通过Handler对象去把该对象传递给主线程,然后再到主线程中去解析JSON数据,最后让这些数据更新我们的UI,因为子线程是无法更新UI的。为了数据的读取的方便性,先将解析后的数据每一条数据保存成一个SearchInfo对象,整个数据就可以保存在SearchInfoList集合中,需要的时候就可以随时去取。
/** * @author mikyou * 根据输入搜索的信息,从网络获得的JSON数据 * 开启一个线程去获取网络数据 * getSearchDataFromNetWork * */ private void getSearchDataFromNetWork() { new Thread(new Runnable() { @Override public void run() { try { JSONObject jsonObject=HttpUtils.send(searchEdit.getText().toString(), null); Message msg=new Message(); msg.obj=jsonObject; msg.what=0x1234; handler.sendMessage(msg); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } } }).start(); } }); }
//搜索网络请求API得到的JSON数据 /** * @author zhongqihong * 利用子线程请求中得到的网络数据,利用Handler来更新 * 主线程(即UI线程) * */ private Handler handler=new Handler(){ public void handleMessage(android.os.Message msg) { if (msg.what==0x1234) { JSONObject object=(JSONObject) msg.obj; //toast("json:----->"+object.toString()); //解析开始:然后把每一个地点信息封装到SearchInfo类中 try { JSONArray array=object.getJSONArray("results"); for (int i = 0; i < array.length(); i++) { JSONObject joObject=array.getJSONObject(i); String name=joObject.getString("name"); JSONObject object2=joObject.getJSONObject("location"); double lat=object2.getDouble("lat"); double lng=object2.getDouble("lng"); String address=joObject.getString("address"); String streetIds=joObject.getString("street_id"); String uids=joObject.getString("uid"); SearchInfo mInfo=new SearchInfo(name, lat, lng, address, streetIds, uids); searchInfoLists.add(mInfo); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } displayInDialog(); }
然后通过displayInDialog方法将解析后的数据,显示在一个列表对话框中。,并且给每个列表项设置点击事件,当点击其中一项,就会把该项的SearchInfo存入的信息读取出经纬度信息,然后通过经纬度信息就可以创建一个经纬度对象,最后把该对象设置给地图,然后该地图就可以定位到该点在地图上的位置,并用一个全景的图标标识,最后点击该图标就会开启一个新的Activity来显示该点的全景或者内容,所以需要将该项的SearchInfo通过Intent对象传递过去。
/** * @author mikyou * 显示搜索后信息的自定义列表项对话框,以及对话框点击事件的处理 * */ private void displayInDialog() { if (searchInfoLists!=null) { AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); builder.setIcon(R.drawable.calibration_arrow) .setTitle("请选择你查询到的地点") .setAdapter(new myDialogListAdapter(), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { final SearchInfo mInfos=searchInfoLists.get(which); uid=mInfos.getUid(); myBaiduMap.setOnMarkerClickListener(new OnMarkerClickListener() { @Override public boolean onMarkerClick(Marker PnoramaMark) { Intent intent=new Intent(MainActivity.this, OtherPnoramaActivity.class); intent.putExtra("info", mInfos); startActivity(intent); return true; } }); addPnoramaLayout(mInfos);// } }).show(); }else{ toast("未查询到相关地点"); } } };
接着通过addPnoramaLayout(mInfos);方法将全景的覆盖物的图标添加到相对应地图的位置
/** * @author mikyou * 添加全景覆盖物,即全景的图标,迅速定位到该地点在地图上的位置 * */ public void addPnoramaLayout(SearchInfo mInfos) { myBaiduMap.clear(); LatLng latLng=new LatLng(mInfos.getLatitude(), mInfos.getLongtiude()); Marker pnoramaMarker=null; OverlayOptions options; BitmapDescriptor mPnoramaIcon=BitmapDescriptorFactory.fromResource(R.drawable.icon_card_streetscape_blue); options=new MarkerOptions().position(latLng).icon(mPnoramaIcon).zIndex(6); pnoramaMarker=(Marker) myBaiduMap.addOverlay(options); MapStatusUpdate msu=MapStatusUpdateFactory.newLatLng(latLng); myBaiduMap.animateMapStatus(msu); }
最后通过全景覆盖物的点击事件就可以开起一个新Activity来显示内景和全景,通过Intent传递一个当前点击的Item项SearchInfo对象
myBaiduMap.setOnMarkerClickListener(new OnMarkerClickListener() { @Override public boolean onMarkerClick(Marker PnoramaMark) { Intent intent=new Intent(MainActivity.this, OtherPnoramaActivity.class); intent.putExtra("info", mInfos); startActivity(intent); return true; } });
四、那么接下来就是对设置街景、内景API操作了,因为通过上一步传来的SearchInfo对象中保存了我们实现内景的所需的数据信息。
/** * @author mikyou * 显示全景 * */ private void initPanoramaView() { SearchInfo info=(SearchInfo) getIntent().getSerializableExtra("info");//获取从MainActivity中传来的SearchInfo对象 String uid=info.getUid();//得到设置内景必需的uid panoramaView=(PanoramaView) findViewById(R.id.panorama); //判断是否有内外景 PanoramaRequest request=PanoramaRequest.getInstance(OtherPnoramaActivity.this); BaiduPoiPanoData poiPanoData=request.getPanoramaInfoByUid(uid);//将该uid设置给内景API panoramaView.setPanoramaImageLevel(ImageDefinition.ImageDefinitionMiddle);//表示展示的内景是中等分辨率,(ImageDefinitionHigh)高分辨率,(ImageDefinitionLow)低分辨率 if (poiPanoData.hasInnerPano()) {//判断该POI是否有内景 panoramaView.setPanoramaByUid(uid, PanoramaView.PANOTYPE_INTERIOR); panoramaView.setIndoorAlbumGone();//除去内景相册 panoramaView.setIndoorAlbumVisible();//将内景相册显示 }else if (poiPanoData.hasStreetPano()) {//判断该POI是否有外景,就只能通过经纬度来显示外景 panoramaView.setPanorama(info.getLongtiude(), info.getLatitude());//没有内景就通过经纬度来展示街景 //Toast.makeText(OtherPnoramaActivity.this, "有该地方的外景,wait", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(OtherPnoramaActivity.this, "sorry,网络不给力无法加载全景", Toast.LENGTH_SHORT).show(); } }
五、全景功能明显也是高耗的功能,展示那么多高清大图需要很多网络流量那么则需要管理全景的生命周期,还是老方法将它的生命周期与该Activity的生命周期进行绑定即可。
@Override protected void onPause() { panoramaView.onPause(); super.onPause(); } @Override protected void onResume() { panoramaView.onResume(); super.onResume(); } @Override protected void onDestroy() { panoramaView.destroy(); super.onDestroy(); }
里全景功能就介绍完毕,最后附上两个Activity的源码:
MainActivity.java:
package com.mikyou.maptest; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.http.client.ClientProtocolException; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Color; import android.graphics.Point; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.view.Window; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import android.widget.ZoomControls; import com.baidu.location.BDLocation; import com.baidu.location.BDLocationListener; import com.baidu.location.LocationClient; import com.baidu.location.LocationClientOption; import com.baidu.mapapi.SDKInitializer; import com.baidu.mapapi.map.BaiduMap; import com.baidu.mapapi.map.BaiduMap.OnMapClickListener; import com.baidu.mapapi.map.BaiduMap.OnMarkerClickListener; import com.baidu.mapapi.map.BitmapDescriptor; import com.baidu.mapapi.map.BitmapDescriptorFactory; import com.baidu.mapapi.map.InfoWindow; import com.baidu.mapapi.map.MapPoi; import com.baidu.mapapi.map.MapStatusUpdate; import com.baidu.mapapi.map.MapStatusUpdateFactory; import com.baidu.mapapi.map.MapView; import com.baidu.mapapi.map.Marker; import com.baidu.mapapi.map.MarkerOptions; import com.baidu.mapapi.map.MyLocationConfiguration; import com.baidu.mapapi.map.UiSettings; import com.baidu.mapapi.map.MyLocationConfiguration.LocationMode; import com.baidu.mapapi.map.MyLocationData; import com.baidu.mapapi.map.OverlayOptions; import com.baidu.mapapi.model.LatLng; import com.mikyou.beans.MarkInfo; import com.mikyou.beans.SearchInfo; import com.mikyou.sensor.MyOrientationListener; import com.mikyou.sensor.MyOrientationListener.onOrientationListener; import com.mikyou.tools.HttpUtils; public class MainActivity extends Activity implements OnClickListener,OnMapClickListener,OnMarkerClickListener{ private MapView mapView=null; private BaiduMap myBaiduMap=null; //修改默认View相关 private View defaultBaiduMapScaleButton,defaultBaiduMapLogo,defaultBaiduMapScaleUnit; //基本地图类型,实时交通,及覆盖物相关 private ImageView mapRoad; private ImageView mapType; private String[] types={"普通地图","卫星地图","热力地图(已关闭)"}; private float current;//放大或缩小的比例系数 private ImageView expandMap;//放大地图控件 private ImageView narrowMap;//缩小地图 private ImageView addMarks;//添加覆盖物控件 private BitmapDescriptor myMarks; private List<MarkInfo> markInfoList; private LinearLayout markLayout; //定位相关 private LocationClient myLocationClient;//专门用于监听位置的客户端对象 private MyLocationListener myListener;//定位监听器对象 private double latitude,longtitude;//经纬度 private BitmapDescriptor myBitmapLocation;//定位的自定义图标 private boolean isFirstIn=true;//设置一个标记,查看是否是第一次 private String locationTextString;//定义的位置的信息 private TextView locationText;//显示定位信息的TextView控件 private MyOrientationListener myOrientationListener; private float myCurrentX; private ImageView selectLocationMode; private ImageView myLocation; private String[] LocationModeString={"罗盘模式","普通模式","跟随模式","3D俯视模式(已关闭)"}; //全景相关 private EditText searchEdit; private ImageView okToSearch; private List<SearchInfo> searchInfoLists; private String uid; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); SDKInitializer.initialize(getApplicationContext()); setContentView(R.layout.activity_main); mapView=(MapView) findViewById(R.id.map_view_test); initMapView(); changeDefaultBaiduMapView(); initMapLocation(); initSearchDestination(); } /** * @author mikyou * 搜索相关 * */ private void initSearchDestination() { searchEdit=(EditText) findViewById(R.id.search_panorama); okToSearch=(ImageView) findViewById(R.id.ok_to_search); okToSearch.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { searchInfoLists=new ArrayList<SearchInfo>(); getSearchDataFromNetWork(); } /** * @author mikyou * 根据输入搜索的信息,从网络获得的JSON数据 * 开启一个线程去获取网络数据 * getSearchDataFromNetWork * */ private void getSearchDataFromNetWork() { new Thread(new Runnable() { @Override public void run() { try { JSONObject jsonObject=HttpUtils.send(searchEdit.getText().toString(), null); Message msg=new Message(); msg.obj=jsonObject; msg.what=0x1234; handler.sendMessage(msg); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } } }).start(); } }); } //搜索网络请求API得到的JSON数据 /** * @author mikyou * 利用子线程请求中得到的网络数据,利用Handler来更新 * 主线程(即UI线程) * */ private Handler handler=new Handler(){ public void handleMessage(android.os.Message msg) { if (msg.what==0x1234) { JSONObject object=(JSONObject) msg.obj; //toast("json:----->"+object.toString()); //解析开始:然后把每一个地点信息封装到SearchInfo类中 try { JSONArray array=object.getJSONArray("results"); for (int i = 0; i < array.length(); i++) { JSONObject joObject=array.getJSONObject(i); String name=joObject.getString("name"); JSONObject object2=joObject.getJSONObject("location"); double lat=object2.getDouble("lat"); double lng=object2.getDouble("lng"); String address=joObject.getString("address"); String streetIds=joObject.getString("street_id"); String uids=joObject.getString("uid"); SearchInfo mInfo=new SearchInfo(name, lat, lng, address, streetIds, uids); searchInfoLists.add(mInfo); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } displayInDialog(); } /** * @author mikyou * 显示搜索后信息的自定义列表项对话框,以及对话框点击事件的处理 * */ private void displayInDialog() { if (searchInfoLists!=null) { AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); builder.setIcon(R.drawable.calibration_arrow) .setTitle("请选择你查询到的地点") .setAdapter(new myDialogListAdapter(), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { final SearchInfo mInfos=searchInfoLists.get(which); uid=mInfos.getUid(); myBaiduMap.setOnMarkerClickListener(new OnMarkerClickListener() { @Override public boolean onMarkerClick(Marker PnoramaMark) { Intent intent=new Intent(MainActivity.this, OtherPnoramaActivity.class); intent.putExtra("info", mInfos); startActivity(intent); return true; } }); addPnoramaLayout(mInfos);// } }).show(); }else{ toast("未查询到相关地点"); } } }; /** * @author mikyou * 添加全景覆盖物,即全景的图标,迅速定位到该地点在地图上的位置 * */ public void addPnoramaLayout(SearchInfo mInfos) { myBaiduMap.clear(); LatLng latLng=new LatLng(mInfos.getLatitude(), mInfos.getLongtiude()); Marker pnoramaMarker=null; OverlayOptions options; BitmapDescriptor mPnoramaIcon=BitmapDescriptorFactory.fromResource(R.drawable.icon_card_streetscape_blue); options=new MarkerOptions().position(latLng).icon(mPnoramaIcon).zIndex(6); pnoramaMarker=(Marker) myBaiduMap.addOverlay(options); MapStatusUpdate msu=MapStatusUpdateFactory.newLatLng(latLng); myBaiduMap.animateMapStatus(msu); } /** * @author mikyou * 将我们搜索的信息来自网络的JSON数据解析后,封装在一个SearchInfo类中 * 然后将这些数据展示在一个自定义的列表项的对话框中,以下就为定义列表项的适配器 * ListAdapter * */ class myDialogListAdapter extends BaseAdapter{ @Override public int getCount() { return searchInfoLists.size(); } @Override public Object getItem(int position) { return getItem(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { SearchInfo mSearchInfo=searchInfoLists.get(position); View view=View.inflate(MainActivity.this, R.layout.dialog_list_item, null); TextView desnameTv=(TextView) view.findViewById(R.id.desname); TextView addressTv=(TextView) view.findViewById(R.id.address); desnameTv.setText(mSearchInfo.getDesname()); addressTv.setText(mSearchInfo.getAddress()); return view; } } /** * @author Mikyou * 初始化定位功能 * */ private void initMapLocation() { myLocationClient=new LocationClient(this);//创建一个定位客户端对象 myListener=new MyLocationListener();//创建一个定位事件监听对象 myLocationClient.registerLocationListener(myListener);//并给该定位客户端对象注册监听事件 //对LocaitonClient进行一些必要的设置 LocationClientOption option=new LocationClientOption(); option.setCoorType("bd09ll");//设置坐标的类型 option.setIsNeedAddress(true);//返回当前的位置信息,如果不设置为true,默认就为false,就无法获得位置的信息 option.setOpenGps(true);//打开GPS option.setScanSpan(1000);//表示1s中进行一次定位请求 myLocationClient.setLocOption(option); useLocationOrientationListener();//调用方向传感器 } /** * @author Mikyou * 定位结合方向传感器,从而可以实时监测到X轴坐标的变化,从而就可以检测到 * 定位图标方向变化,只需要将这个动态变化的X轴的坐标更新myCurrentX值, * 最后在MyLocationData data.driection(myCurrentX); * */ private void useLocationOrientationListener() { myOrientationListener=new MyOrientationListener(MainActivity.this); myOrientationListener.setMyOrientationListener(new onOrientationListener() { @Override public void onOrientationChanged(float x) {//监听方向的改变,方向改变时,需要得到地图上方向图标的位置 myCurrentX=x; System.out.println("方向:x---->"+x); } }); } /** * @author mikyou * 获取位置信息的客户端对象的监听器类MyLocationListener * */ class MyLocationListener implements BDLocationListener{ @Override public void onReceiveLocation(BDLocation location) { //得到一个MyLocationData对象,需要将BDLocation对象转换成MyLocationData对象 MyLocationData data=new MyLocationData.Builder() .accuracy(location.getRadius())//精度半径 .direction(myCurrentX)//方向 .latitude(location.getLatitude())//经度 .longitude(location.getLongitude())//纬度 .build(); myBaiduMap.setMyLocationData(data); //配置自定义的定位图标,需要在紧接着setMyLocationData后面设置 //调用自定义定位图标 changeLocationIcon(); latitude=location.getLatitude();//得到当前的经度 longtitude=location.getLongitude();//得到当前的纬度 //toast("经度:"+latitude+" 纬度:"+longtitude); if (isFirstIn) {//表示用户第一次打开,就定位到用户当前位置,即此时只要将地图的中心点设置为用户此时的位置即可 getMyLatestLocation(latitude,longtitude);//获得最新定位的位置,并且地图的中心点设置为我的位置 isFirstIn=false;//表示第一次才会去定位到中心点 locationTextString=""+location.getAddrStr();//这里得到地址必须需要在设置LocationOption的时候需要设置isNeedAddress为true; toast(locationTextString); locationText.setText(locationTextString); } } } /** * @author zhongqihong * 获得最新定位的位置,并且地图的中心点设置为我的位置 * */ private void getMyLatestLocation(double lat,double lng) { LatLng latLng=new LatLng(lat, lng);//创建一个经纬度对象,需要传入当前的经度和纬度两个整型值参数 MapStatusUpdate msu=MapStatusUpdateFactory.newLatLng(latLng);//创建一个地图最新更新的状态对象,需要传入一个最新经纬度对象 myBaiduMap.animateMapStatus(msu);//表示使用动画的效果传入,通过传入一个地图更新状态对象,然后利用百度地图对象来展现和还原那个地图更新状态,即此时的地图显示就为你现在的位置 } /** * @author zhongqihong * 自定义定位图标 * */ private void changeLocationIcon() { myBitmapLocation=BitmapDescriptorFactory .fromResource(R.drawable.ic_launcher);//引入自己的图标 if (isFirstIn) {//表示第一次定位显示普通模式 MyLocationConfiguration config=new MyLocationConfiguration(LocationMode.NORMAL, true, myBitmapLocation); myBaiduMap.setMyLocationConfigeration(config); } } /** * @author zhongqihong * 初始化地图的View * */ private void initMapView() { registerAllIds(); registerAllEvents(); } private void registerAllIds() { mapRoad=(ImageView) findViewById(R.id.road_condition); mapType=(ImageView) findViewById(R.id.map_type); expandMap=(ImageView) findViewById(R.id.add_scale); narrowMap=(ImageView) findViewById(R.id.low_scale); addMarks=(ImageView) findViewById(R.id.map_marker); markLayout=(LinearLayout) findViewById(R.id.mark_layout); locationText=(TextView) findViewById(R.id.mylocation_text); selectLocationMode=(ImageView) findViewById(R.id.map_location); myLocation=(ImageView) findViewById(R.id.my_location); } private void registerAllEvents() { mapRoad.setOnClickListener(this); mapType.setOnClickListener(this); expandMap.setOnClickListener(this); narrowMap.setOnClickListener(this); addMarks.setOnClickListener(this); selectLocationMode.setOnClickListener(this); myLocation.setOnClickListener(this); } /** * @author mikyou * 除去百度地图上的默认控件 * */ private void changeDefaultBaiduMapView() { changeInitialzeScaleView();//改变默认百度地图初始加载的地图比例 //设置隐藏缩放和扩大的百度地图的默认的比例按钮 for (int i = 0; i < mapView.getChildCount(); i++) {//遍历百度地图中的所有子View,找到这个扩大和缩放的按钮控件View,然后设置隐藏View即可 View child=mapView.getChildAt(i); if (child instanceof ZoomControls) { defaultBaiduMapScaleButton=child;//该defaultBaiduMapScaleButton子View是指百度地图默认产生的放大和缩小的按钮,得到这个View break; } } defaultBaiduMapScaleButton.setVisibility(View.GONE);//然后将该View的Visiblity设为不存在和不可见,即隐藏 defaultBaiduMapLogo =mapView.getChildAt(1);//该View是指百度地图中默认的百度地图的Logo,得到这个View defaultBaiduMapLogo.setPadding(300, -10, 100, 100);//设置该默认Logo View的位置,因为这个该View的位置会影响下面的刻度尺单位View显示的位置 mapView.removeViewAt(1);//最后移除默认百度地图的logo View defaultBaiduMapScaleUnit=mapView.getChildAt(2);//得到百度地图的默认单位刻度的View defaultBaiduMapScaleUnit.setPadding(100, 0, 115,200);//最后设置调整百度地图的默认单位刻度View的位置 } /** * @author mikyou * 改变默认初始化的地图的比例 * */ private void changeInitialzeScaleView() { myBaiduMap=mapView.getMap();//改变百度地图的放大比例,让首次加载地图就开始扩大到500米的距离,获得百度地图对象 MapStatusUpdate factory=MapStatusUpdateFactory.zoomTo(15.0f); myBaiduMap.animateMapStatus(factory); } /** * @author mikyou * 管理地图的生命周期 * */ protected void onDestroy() { super.onDestroy(); //在Activity执行onDestory时执行mapView(地图)生命周期管理 mapView.onDestroy(); } @Override protected void onStart() {//当Activity调用onStart方法,开启定位以及开启方向传感器,即将定位的服务、方向传感器和Activity生命周期绑定在一起 myBaiduMap.setMyLocationEnabled(true);//开启允许定位 if (!myLocationClient.isStarted()) { myLocationClient.start();//开启定位 } //开启方向传感器 myOrientationListener.start(); super.onStart(); } @Override protected void onStop() {//当Activity调用onStop方法,关闭定位以及关闭方向传感器 myBaiduMap.setMyLocationEnabled(false); myLocationClient.stop();//关闭定位 myOrientationListener.stop();//关闭方向传感器 super.onStop(); } @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); //在Activity执行onResume是执行MapView(地图)生命周期管理 mapView.onResume(); } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); mapView.onPause(); } //点击事件相关 @Override public void onClick(View v) { switch (v.getId()) { case R.id.road_condition://是否打开实时交通 switchRoadCondition(); break; case R.id.map_type://选择地图的类型 selectMapType(); break; case R.id.add_scale://放大地图比例 expandMapScale(); break; case R.id.low_scale://缩小地图比例 narrowMapScale(); break; case R.id.map_marker://添加覆盖物 addMapMarks(); break; case R.id.my_location://定位功能,需要用到LocationClient进行定位 //BDLocationListener getMyLatestLocation(latitude,longtitude); break; case R.id.map_location://选择定位模式 selectLocation(); break; default: break; } } /** * @author mikyou * 选择定位的模式 * */ private void selectLocation() { AlertDialog.Builder builder2=new AlertDialog.Builder(this); builder2.setIcon(R.drawable.track_collect_running) .setTitle("请选择定位的模式") .setItems(LocationModeString, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String mode=LocationModeString[which]; if (mode.equals("罗盘模式")) { MyLocationConfiguration config=new MyLocationConfiguration(LocationMode.COMPASS, true, myBitmapLocation); myBaiduMap.setMyLocationConfigeration(config); }else if (mode.equals("跟随模式")) { MyLocationConfiguration config=new MyLocationConfiguration(LocationMode.FOLLOWING, true, myBitmapLocation); myBaiduMap.setMyLocationConfigeration(config); }else if (mode.equals("普通模式")) { MyLocationConfiguration config=new MyLocationConfiguration(LocationMode.NORMAL, true, myBitmapLocation); myBaiduMap.setMyLocationConfigeration(config); }else if (mode.equals("3D俯视模式(已关闭)")||mode.equals("3D俯视模式(已打开)")) { if (mode.equals("3D俯视模式(已打开)")) { UiSettings mUiSettings = myBaiduMap.getUiSettings(); mUiSettings.setCompassEnabled(true); LocationModeString[which]="3D俯视模式(已关闭)"; toast("3D模式已关闭"); }else{ MyLocationConfiguration config=new MyLocationConfiguration(LocationMode.COMPASS, true, myBitmapLocation); myBaiduMap.setMyLocationConfigeration(config); MyLocationConfiguration config2=new MyLocationConfiguration(LocationMode.NORMAL, true, myBitmapLocation); myBaiduMap.setMyLocationConfigeration(config2); LocationModeString[which]="3D俯视模式(已打开)"; toast("3D模式已打开"); } } } }).show(); } /** * @author Mikyou * 添加覆盖物 * */ private void addMapMarks() { initMarksData(); myBaiduMap.clear();//先清除一下图层 LatLng latLng=null; Marker marker=null; OverlayOptions options; myMarks=BitmapDescriptorFactory.fromResource(R.drawable.mark);//引入自定义的覆盖物图标,将其转化成一个BitmapDescriptor对象 //遍历MarkInfo的List一个MarkInfo就是一个Mark for (int i = 0; i < markInfoList.size(); i++) { //经纬度对象 latLng=new LatLng(markInfoList.get(i).getLatitude(), markInfoList.get(i).getLongitude());//需要创建一个经纬对象,通过该对象就可以定位到处于地图上的某个具体点 //图标 options=new MarkerOptions().position(latLng).icon(myMarks).zIndex(6); marker=(Marker) myBaiduMap.addOverlay(options);//将覆盖物添加到地图上 Bundle bundle=new Bundle();//创建一个Bundle对象将每个mark具体信息传过去,当点击该覆盖物图标的时候就会显示该覆盖物的详细信息 bundle.putSerializable("mark", markInfoList.get(i)); marker.setExtraInfo(bundle); } MapStatusUpdate msu=MapStatusUpdateFactory.newLatLng(latLng);//通过这个经纬度对象,地图就可以定位到该点 myBaiduMap.animateMapStatus(msu); } /** * @author mikyou * 初始化覆盖物信息数据 * */ private void initMarksData() { markInfoList=new ArrayList<MarkInfo>(); markInfoList.add(new MarkInfo(32.079254, 118.787623, R.drawable.pic1, "英伦贵族小旅馆", "距离209米", 1888)); markInfoList.add(new MarkInfo(32.064355, 118.787624, R.drawable.pic2, "沙井国际高级会所", "距离459米", 388)); markInfoList.add(new MarkInfo(28.7487420000, 115.8748860000, R.drawable.pic4, "华东交通大学南区", "距离5米", 888)); markInfoList.add(new MarkInfo(28.7534890000, 115.8767960000, R.drawable.pic3, "华东交通大学北区", "距离10米", 188)); myBaiduMap.setOnMarkerClickListener(this); myBaiduMap.setOnMapClickListener(this); } /** * @author mikyou * 放大地图的比例 * */ private void narrowMapScale() { current-=0.5f; MapStatusUpdate msu=MapStatusUpdateFactory.zoomTo(15.0f+current); myBaiduMap.animateMapStatus(msu); } /** *@author mikyou *缩小地图的比例 * */ private void expandMapScale() { current+=0.5f; MapStatusUpdate msu2=MapStatusUpdateFactory.zoomTo(15.0f+current); myBaiduMap.animateMapStatus(msu2); } /** * @author mikyou * 选择地图的类型 * */ private void selectMapType() { AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); builder.setIcon(R.drawable.icon) .setTitle("请选择地图的类型") .setItems(types, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String select=types[which]; if (select.equals("普通地图")) { myBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL); }else if (select.equals("卫星地图")) { myBaiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE); }else if (select.equals("热力地图(已关闭)")||select.equals("热力地图(已打开)")) { if(myBaiduMap.isBaiduHeatMapEnabled()) { myBaiduMap.setBaiduHeatMapEnabled(false); Toast.makeText(MainActivity.this, "热力地图已关闭", 0).show(); types[which]="热力地图(已关闭)"; }else { myBaiduMap.setBaiduHeatMapEnabled(true); Toast.makeText(MainActivity.this, "热力地图已打开", 0).show(); types[which]="热力地图(已打开)"; } } } }).show(); } /** * @author mikyou * 是否打开实时交通 * */ private void switchRoadCondition() { if (myBaiduMap.isTrafficEnabled()) {//如果是开着的状态,当点击后,就会出关闭状态 myBaiduMap.setTrafficEnabled(false); mapRoad.setImageResource(R.drawable.main_icon_roadcondition_off); }else{//如果是的关闭的状态,当点击后,就会处于开启的状态 myBaiduMap.setTrafficEnabled(true); mapRoad.setImageResource(R.drawable.main_icon_roadcondition_on); } } /** * @author mikyou * 覆盖物的点击事件 * */ @Override public boolean onMarkerClick(Marker marker) { Bundle bundle= marker.getExtraInfo(); MarkInfo MyMarker=(MarkInfo) bundle.getSerializable("mark"); ImageView iv=(ImageView) markLayout.findViewById(R.id.mark_image); TextView distanceTv=(TextView) markLayout.findViewById(R.id.distance); TextView nameTv=(TextView) markLayout.findViewById(R.id.name); TextView zanNumsTv=(TextView) markLayout.findViewById(R.id.zan_nums); iv.setImageResource(MyMarker.getImageId()); distanceTv.setText(MyMarker.getDistance()+""); nameTv.setText(MyMarker.getName()); zanNumsTv.setText(MyMarker.getZanNum()+""); //初始化一个InfoWindow initInfoWindow(MyMarker,marker); markLayout.setVisibility(View.VISIBLE); return true; } /** *@author mikyou *初始化出一个InfoWindow * * */ private void initInfoWindow(MarkInfo MyMarker,Marker marker) { // TODO Auto-generated method stub InfoWindow infoWindow; //InfoWindow中显示的View内容样式,显示一个TextView TextView infoWindowTv=new TextView(MainActivity.this); infoWindowTv.setBackgroundResource(R.drawable.location_tips); infoWindowTv.setPadding(30, 20, 30, 50); infoWindowTv.setText(MyMarker.getName()); infoWindowTv.setTextColor(Color.parseColor("#FFFFFF")); final LatLng latLng=marker.getPosition(); Point p=myBaiduMap.getProjection().toScreenLocation(latLng);//将地图上的经纬度转换成屏幕中实际的点 p.y-=47;//设置屏幕中点的Y轴坐标的偏移量 LatLng ll=myBaiduMap.getProjection().fromScreenLocation(p);//把修改后的屏幕的点有转换成地图上的经纬度对象 /** * @author mikyou * 实例化一个InfoWindow的对象 * public InfoWindow(View view,LatLng position, int yOffset)通过传入的 view 构造一个 InfoWindow, 此时只是利用该view生成一个Bitmap绘制在地图中,监听事件由开发者实现。 * 参数: * view - InfoWindow 展示的 view * position - InfoWindow 显示的地理位置 * yOffset - InfoWindow Y 轴偏移量 * */ infoWindow=new InfoWindow(infoWindowTv, ll, 10); myBaiduMap.showInfoWindow(infoWindow);//显示InfoWindow } /** * @author mikyou * 给整个地图添加的点击事件 * */ @Override public void onMapClick(LatLng arg0) {//表示点击地图其他的地方使得覆盖物的详情介绍的布局隐藏,但是点击已显示的覆盖物详情布局上,则不会消失,因为在详情布局上添加了Clickable=true //由于事件的传播机制,因为点击事件首先会在覆盖物布局的父布局(map)中,由于map是可以点击的,map则会把点击事件给消费掉,如果加上Clickable=true表示点击事件由详情布局自己处理,不由map来消费 markLayout.setVisibility(View.GONE); myBaiduMap.hideInfoWindow();//隐藏InfoWindow } @Override public boolean onMapPoiClick(MapPoi arg0) { return false; } public void toast(String str){ Toast.makeText(MainActivity.this, str, 0).show(); } }
OtherPnoramaActivity.java:
package com.mikyou.maptest; import com.baidu.lbsapi.BMapManager; import com.baidu.lbsapi.model.BaiduPoiPanoData; import com.baidu.lbsapi.panoramaview.PanoramaRequest; import com.baidu.lbsapi.panoramaview.PanoramaView; import com.baidu.lbsapi.panoramaview.PanoramaView.ImageDefinition; import com.mikyou.beans.SearchInfo; import com.mikyou.tools.SystemStatusManager; import android.os.Build; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.Window; import android.view.WindowManager; import android.widget.Toast; public class OtherPnoramaActivity extends Activity { private double latitude,longtiude; private PanoramaView panoramaView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setTranslucentStatus(); setContentView(R.layout.activity_other_pnorama); initPanoramaView(); } @Override protected void onPause() { panoramaView.onPause(); super.onPause(); } @Override protected void onResume() { panoramaView.onResume(); super.onResume(); } @Override protected void onDestroy() { panoramaView.destroy(); super.onDestroy(); } /** * @author mikyou * 显示全景 * */ private void initPanoramaView() { SearchInfo info=(SearchInfo) getIntent().getSerializableExtra("info");//获取从MainActivity中传来的SearchInfo对象 String uid=info.getUid();//得到设置内景必需的uid panoramaView=(PanoramaView) findViewById(R.id.panorama2); //判断是否有内外景 PanoramaRequest request=PanoramaRequest.getInstance(OtherPnoramaActivity.this); BaiduPoiPanoData poiPanoData=request.getPanoramaInfoByUid(uid);//将该uid设置给内景API panoramaView.setPanoramaImageLevel(ImageDefinition.ImageDefinitionMiddle);//表示展示的内景是中等分辨率,(ImageDefinitionHigh)高分辨率,(ImageDefinitionLow)低分辨率 if (poiPanoData.hasInnerPano()) {//判断该POI是否有内景 panoramaView.setPanoramaByUid(uid, PanoramaView.PANOTYPE_INTERIOR); panoramaView.setIndoorAlbumGone();//除去内景相册 panoramaView.setIndoorAlbumVisible();//将内景相册显示 }else if (poiPanoData.hasStreetPano()) {//判断该POI是否有外景,就只能通过经纬度来显示外景 panoramaView.setPanorama(info.getLongtiude(), info.getLatitude());//没有内景就通过经纬度来展示街景 //Toast.makeText(OtherPnoramaActivity.this, "有该地方的外景,wait", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(OtherPnoramaActivity.this, "sorry,网络不给力无法加载全景", Toast.LENGTH_SHORT).show(); } } private void setTranslucentStatus() { if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){ Window win=getWindow(); WindowManager.LayoutParams winParams=win.getAttributes(); final int bits=WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; winParams.flags |=bits; win.setAttributes(winParams); } SystemStatusManager tintManager = new SystemStatusManager(this); tintManager.setStatusBarTintEnabled(true); tintManager.setStatusBarTintResource(0); tintManager.setNavigationBarTintEnabled(true); } }
运行效果: