仿百度地图街景实现

使用过百度地图的同学知道,它有个街景功能,可以看到许多地方的实景。这里就其街景内容的实现,进行下学习。

百度地图SDK的官网上可以看到,百度对开发者提供了很多相关的内容,方便我们进行学习。关于SDK的使用方法,包括jar包导入,*.so 动态库的添加位置及AndroidManifest文件的配置不做为我们这里讨论的内容,官方文档已经介绍的很详细,不做无聊的搬运工。

效果图

这里我们首先预览下,今天最终要实现的效果图





如图所示,我们这里的实现,就是两个页面的内容,一个是基础的地图MapView,一个是街景地图PanoView。接下来,就这两个页面(Activity)分别展开来说。(由于GIF图片大小限制,效果不是很理想,文章结尾有源码地址,可以自己跑一下看一下效果先)

地图MapView实现

地图MapView的简单显示

布局文件

<?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"
    android:orientation="vertical">

    <com.baidu.mapapi.map.MapView
        android:id="@+id/bmapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true" />

    </LinearLayout>

Application

一般情况下,我们的应用程序都会有一个继承自Application的类,用于实现一些初始化的方法,这里可以在Application里执行一些百度地图初始化的工作,这也是官方提倡的方式。

public void onCreate() {
        super.onCreate();
        mInstance = this;     SDKInitializer.initialize(getApplicationContext());
        }

Activity

在Activity的OnCreate方法中实现

setContentView(R.layout.activity_mapview);
mMapView = (MapView) findViewById(R.id.bmapView);

上面这样一段简单的代码,就可以在Activity中显示出一个MapView,也就是我们最熟悉的地图页面,是不是很简单,就像我们显示一个TextView一样。

这里说明写一下,按照官方的API指导文档,使用MapView等百度地图SDK所提供的各种实现,是需要去申请相关的key的,申请的方法在官网有着详细的介绍,这里就不再粘贴复制了;很多同学在使用MapView的时候发现,程序运行后地图没有显示,显示的都是一些方格子,这往往是由于key没有申请,或申请的方式不当造成的

MapView显示到当前位置

每次打开百度地图,都会自动定位到我们当前所在的位置,或者是我们搜索某个特定的地方作为新的位置,整个地图所呈现的区域都是新位置周边的环境。这里,关于地图的定位和搜索的相关实现内容,就不展开来说,不当做此次的重点。

假设我们已通过定位(或者是搜索),定位了到了一个位置

**
 * 假设我们当前的位置在此
 */
private final double latitude = 39.963175;
private final double longitude = 116.400244;

这个位置按照新闻里常听到的说法就是,东经116.40度,北纬39.96度,位于北京市东城区旧鼓楼大街丙1号。

接下来,我们要做的就是将MapView的视图更新到我们“定位”的位置,这个位置周边的地图才是我们关心的。

mBaiduMap = mMapView.getMap();
        //定义Maker坐标点
        point = new LatLng(latitude, longitude);
        //定义地图状态
        final MapStatus mMapStatus = new MapStatus.Builder()
                .target(point)
                .zoom(18)
                .build();
        //定义MapStatusUpdate对象,以便描述地图状态将要发生的变化
        MapStatusUpdate mMapStatusUpdate = MapStatusUpdateFactory.newMapStatus(mMapStatus);
        //改变地图状态
        mBaiduMap.setMapStatus(mMapStatusUpdate);

这里的mBaiduMap 是一个BaiduMap的实例,通过MapView的getMap方法即可获得。我们对地图的各种操作,设置属性都是基于这个实例进行。

通过上面的代码,我们就可以将MapView的视图更新到我们所想要的位置了。

添加View到MapView

添加Marker

按照百度地图API的说法,我们添加到地图上的小图标统一称为Marker。

//构建Marker图标
        bitmap = BitmapDescriptorFactory
                .fromResource(R.drawable.icon_markc);

        //构建MarkerOption,用于在地图上添加Marker
        option = new MarkerOptions()
                .position(point)
                .icon(bitmap);
        //在地图上添加Marker,并显示
        mBaiduMap.addOverlay(option);

通过上面的实现,我们就可以将一个小图标添加到地图层,作为标记。我们日常使用地图时,所搜周边后呈现的一系列小圆点就是如此(如下图)

ShowInfoWindow使用

最后一步,实现显示街景缩略图的那个小弹框。

这里首先自定义一下我们要添加到地图层的View。

view = LayoutInflater.from(mContext).inflate(R.layout.pano_overlay, null);
        pic = (ImageView) view.findViewById(R.id.panoImageView);
view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(mContext, PanoDemoMain.class);
                intent.putExtra("latitude", latitude);
                intent.putExtra("longitude", longitude);
                startActivity(intent);
            }
        });

这里pic这个ImageView用于显示我们要展示的街景缩略图。pano_overlay是整个弹框的布局,很简单,这里就不贴代码了。

同时,我们为这个自定义View设置点击事件,方便我们跳转到PanoView街景地图页面,并且将当前位置传递过去。

由于祖国地大物博,所以街景的覆盖并非百分之百,所以说,不是每个地方都有街景可以显示,有些鸟不拉屎的地方是看不到的。那我们怎么知道什么地方有街景呢?API为我们提供了很好的检测方法

new Thread(new Runnable() {
            @Override
            public void run() {
                PanoramaRequest request = PanoramaRequest.getInstance(mContext);
                BaiduPanoData locationPanoData = request.getPanoramaInfoByLatLon(longitude, latitude);
                //开发者可以判断是否有外景(街景)
                if (locationPanoData.hasStreetPano()) {
                    String url = baseUrl + locationPanoData.getPid();
                    Message message = new Message();
                    message.what = 0x01;
                    message.obj = url;
                    handler.sendMessage(message);
                }

            }
        }).start();

这样,我们就可以根据当前位置,先检测一下是否有街景可以显示。这里,如果当前位置有街景,我们就通过Handler通知主线程去更新UI

private class myHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 0x01) {
                String url = (String) msg.obj;
                Glide.with(mContext).load(url).into(pic);
                InfoWindow mInfoWindow = new InfoWindow(view, point, -57);
                //显示InfoWindow
                mBaiduMap.showInfoWindow(mInfoWindow);
            }
        }
    }

这里看一下,InfoWindow的说明及其构造函数

public class InfoWindow extends java.lang.Object

在地图中显示一个信息窗口,可以设置一个View作为该窗口的内容,也可以设置一个 BitmapDescriptor 作为该窗口的内容。


public InfoWindow(View view, LatLng position, int yOffset)

/**
通过传入的 view 构造一个 InfoWindow, 此时只是利用该view
生成一个Bitmap绘制在地图中,监听事件由开发者实现。
Parameters:
view - InfoWindow 展示的 view
position - InfoWindow 显示的地理位置
yOffset - InfoWindow Y 轴偏移量
*/

在Handler的handleMessage方法中,我们通过返回的url加载图片,并将自定义的弹框View显示到之前一步添加的marker偏上一点的地方(这就是InfoWindow的构造函数中-57的意义)

关于这个加载图片的URL,可以参考这里静态图API

这样,就实现了MapView页面所有的内容。通过点击InfoWindow,就可以跳转到PanoView所在的界面去查看街景地图。

接下来,我们将介绍PanoView街景地图的实现。

街景地图PanoViewActivity实现

街景地图PanoView基础

街景地图PanoView的显示和基础地图MapView十分相似

首先是在布局文件中定义view

<com.baidu.lbsapi.panoramaview.PanoramaView
            android:id="@+id/panorama"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clickable="true"
            android:visibility="visible" />

Activity的OnCreate方法中

PanoDemoApplication app = (PanoDemoApplication) this.getApplication();
        if (app.mBMapManager == null) {
            app.mBMapManager = new BMapManager(app);
            app.mBMapManager.init(new PanoDemoApplication.MyGeneralListener());
        }

mPanoView = (PanoramaView) findViewById(R.id.panorama);
mPanoView.setPanorama(longitude, latitude);

这里同样需要的是在Application类中做一些初始化工作;对我们所使用key的有效性进行检测。

public void initEngineManager(Context context) {
        if (mBMapManager == null) {
            mBMapManager = new BMapManager(context);
        }

        if (!mBMapManager.init(new MyGeneralListener())) {
            Toast.makeText(PanoDemoApplication.getInstance().getApplicationContext(), "BMapManager  初始化错误!",
                    Toast.LENGTH_LONG).show();
        }
    }

// 常用事件监听,用来处理通常的网络错误,授权验证错误等
    static class MyGeneralListener implements MKGeneralListener {

        @Override
        public void onGetPermissionState(int iError) {
            // 非零值表示key验证未通过
            if (iError != 0) {
                // 授权Key错误:
                Toast.makeText(PanoDemoApplication.getInstance().getApplicationContext(),
                        "请在AndoridManifest.xml中输入正确的授权Key,并检查您的网络连接是否正常!error: " + iError, Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(PanoDemoApplication.getInstance().getApplicationContext(), "key认证成功", Toast.LENGTH_LONG)
                        .show();
            }
        }
    }

同时在Activity里也需要做一些初始化的工作,最后就是通过PanoView的setPanorama()方法实现街景的显示。

关于这里用到的setPanorama(),根据API我们可以看到

public void setPanorama(java.lang.String pid)
//根据全景pid值切换全景场景

public void setPanorama(int x,int y)
//根据百度墨卡托投影坐标切换全景场景

public void setPanorama(double longitude,double latitude)
//根据百度经纬度坐标切换全景场景

public void setPanoramaByUid(java.lang.String uid,
                    int panoType)
//根据uid值切换全景场景

也就是说,不仅通过经纬度,而且可以通过别的方式实现街景地图的功能。这里我们就使用了大家最熟悉的经纬度,对于别的实现方式有兴趣的同学,可以自己去探索一下。

将地图MapView展示在街景PanoView上面

如图所示,将一个MapView显示在PanoView之上;很自然的我们会写出下面的布局方式:

<RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.baidu.lbsapi.panoramaview.PanoramaView
            android:id="@+id/panorama"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clickable="true"
            android:visibility="visible" />

        <LinearLayout
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_marginBottom="8dp"
            android:layout_marginLeft="8dp"
            android:background="#00ffffff">

            <com.baidu.mapapi.map.MapView
                android:id="@+id/bmapView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:clickable="true"
                android:padding="20dp" />
        </LinearLayout>
    </RelativeLayout>

这样,我们在整个PanoView的左下角定义一个60x60大小的view用于显示一个MapView。

实现对街景视图操作的监听

街景SDK为我们提供了PanoramaViewListener这个接口,可以实现对从街景视图开始绘制到完成绘制,对街景地图的操作(如点击,旋转)的监听。

这里我们重点看一下onMessage(String msgName, int msgType)这个回调方法。

public void onMessage(String msgName, int msgType) {
                Log.e(LTAG, "msgName--->" + msgName + ", msgType--->" + msgType);
                switch (msgType) {
                    case 8213:
                        //旋转
                        Log.e(PanoViewActivity.class.getSimpleName(), "now,the heading is " + mPanoView.getPanoramaHeading());
                        Message message = new Message();
                        message.what = ACTION_DRAG;
                        message.arg1 = (int) mPanoView.getPanoramaHeading();
                        handler.sendMessage(message);
                        break;
                    case 12302:
                        //点击
                        Log.e(PanoViewActivity.class.getSimpleName(), "clicked");
                        Message msg = new Message();
                        msg.what = ACTION_CLICK;
                        handler.sendMessage(msg);
                        break;
                    default:
                        break;
                }

            }

这里不得不吐槽一下,官方所提供的API文档,对这个onMessage回调方法中的参数居然没有任何有价值的解释。这里的8213及12302完全是通过打印日志自己总结出的规律。

这样,我们对于不同的操作,就可以通过Handler实现不同的UI效果。我们看一下handler的实现:

private class MyHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case ACTION_CLICK:
                    if (titleVisible) {
                        titleVisible = false;
                        textTitle.startAnimation(animationHide);
                        textTitle.setVisibility(View.GONE);
                        sv.setVisibility(View.GONE);
                    } else {
                        titleVisible = true;
                        textTitle.startAnimation(animationShow);
                        textTitle.setVisibility(View.VISIBLE);
                        sv.setVisibility(View.VISIBLE);

                    }

                    break;
                case ACTION_DRAG:
                    float heading = (float) msg.arg1;

                    mBaiduMap.clear();

                    //构建MarkerOption,用于在地图上添加Marker
                    option = new MarkerOptions()
                            .position(point)
                            .rotate(360-heading)
                            .icon(bitmap);
                    //在地图上添加Marker,并显示
                    mBaiduMap.addOverlay(option);
                    break;
                default:
                    break;
            }
        }

    }

这里的处理就分两种情况:

  • 点击事件

我们仿照百度地图的样式,实现标题栏及MapView的隐藏,并添加动画,这样可以方便用户全屏更清晰的观察街景内容。

  • 旋转事件

上面我们说过对MapView添加Marker的方法,这里就派上用场了。随着我们对PanoView的不断拖拽旋转,通过其getPanoramaHeading() 可以得到当前视角的偏航角。

在UI线程中,我们可以通过不断移除和添加Marker,并设置不同的marker的偏转角度,从而实现一种在左下方小地图上呈现我们当前视角的效果。

好了,这样就简单模仿了一下百度地图街景的部分实现功能,由于UI资源所限制,部分效果并非完全一致,这里只是学习下而已。

代码已上传至github,点这里即可查看。


时间: 2024-08-09 19:50:46

仿百度地图街景实现的相关文章

js仿百度地图拖拽、缩放、添加图层功能(原创)

最近项目中完成的需求,仿百度地图中的功能: 要求:1.底层图可以拖拽.缩放.     2.拖拽一个图标,在底层图上对应位置添加一个标注点,该标注点位置要随底层图移动. 3.添加的标注点,可以拖动,删除. 主要知识点和难点就是各个浏览器的点击.拖拽.缩放事件兼容性,对js运动属性.运动偏移位置的了解,以及js面向对象的应用. 这里跟大家分享一下完成后的代码. html代码主要知识点就是运动元素和底层元素的相对绝对定位,css代码不再贴出: <div id="warp" style=

ArcGIS JS 学习笔记1 用ArcGIS JS 实现仿百度地图的距离量测和面积量测

一.开篇 在博客注册了三年,今天才决定写第一篇博客,警告自己不要懒!!! 二.关于ArcGIS JS 版本选择 在写这篇博客时ArcGIS JS 4.0正式版已经发布.它和3.x版本的不同是,Map不在是一个控件,而真的只是一张“图”,Map(4.0版本)需要在一个View里面来展示,在MapView里面就是一张平面图,在SceneView里面就一张三维地图.同一张地图在不同的View里面就可以呈现出不同的效果.但是4.0版本才是一个最初的版本,还有很多3.x有的功能没有被加入到其中.所以我打算

模仿百度地图的LBS服务——离线地图篇 Part 2 (v 3.1.1)

一.前言 转载请标明出处:http://blog.csdn.net/wlwlwlwl015/article/details/41492031 这一篇blog写的真心不容易,我只想说我这种菜鸟去高仿百度地图去做LBS服务真心有点作死,期间本想放弃,做简单点算了,但不能说服自己.最后通过F6去一行一行的debug(新手朋友注意这是最好的解决问题的方式没有之一),最后成功完成了核心的功能.上一篇blog高仿了百度地图离线地图模块中的"城市列表"部分(模仿百度地图的LBS服务--离线地图篇 P

iOS-Andriod百度地图仿百度外卖-饿了么-选择我的地址-POI检索/

http://zanderzhang.gitcafe.io/2015/09/19/iOS-Andriod百度地图仿百度外卖-饿了么-选择我的地址-POI检索/ 百度外卖选择送货地址: 饿了么选择送货地址: 百度地图api官网 第一个图,就是放一个UIImageView在MapView的中间,然后我们拖动的时候下面地图在跑. -(void)addMiddleImage{ UIImageView *imaV=[UIImageView new]; imaV.center=_mapView.center

Android中级篇之百度地图SDK v3.5.0-一步一步带你仿各大主流APP地图定位移动选址功能

定位+移动选址 百学须先立志-学前须知: 我们经常在各大主流APP上要求被写上地址,如百度外卖.爱鲜蜂收货地址等等:其中他们大多数是可以让我们在地图上移动选址.就如下面这段GIF演示的一样: 尽信书,不如无书-能学到什么? 1.地图状态MapStatus类及监听setOnMapStatusChangeListener 2.定位LocationClient类 3.反地理编码GeoCoder类 工欲善其事必先利其器-申请Key 百度地图访问应用(AK)申请地址:http://lbsyun.baidu

利用百度地图Android sdk高仿微信发送位置功能

接触了百度地图开发平台半个月了,这2天试着模仿了微信给好友发送位置功能,对百度地图的操作能力又上了一个台阶 (假设须要完整demo.请评论留下邮箱) (眼下源代码已经不发送,假设须要源代码.加qq316701116,不喜勿扰)    我在实现这个功能的时候,遇到一些困难,可能也是别人将会遇到的困难,特在此列出 1.在微信发送功能中,无论用户怎样拖拽地图.总有个覆盖物固定了MapView中央,怎么实现?     事实上这非常easy实现.仅仅要MapView的布局文件里.将一个ImageView覆

iOS 使用百度地图,仿滴滴打车的定位方法。拖动时时定位

这里的思路: (1)把图片放到屏幕的中间,这样在拖动的时候就不会跟随着地图移动了. (2)百度地图提供了,View坐标和地理坐标转换的方法.正式这个方法的存在,方便我们及时的获取拖动后的,屏幕中间的图片所在位置的经纬度. 当拖动地图的时候,定位的图片一直在屏幕的中央,当拖动停止的时候会显示出具体的信息 #import "HouseTypeMapVC.h" @interface HouseTypeMapVC ()<BMKMapViewDelegate,BMKLocationServ

百度地图API和高德地图API资料集锦

[高德地图API]从零开始学高德JS API(五)路线规划--驾车|公交|步行 [高德地图API]从零开始学高德JS API(四)搜索服务--POI搜索|自动完成|输入提示|行政区域|交叉路口|自有数据检索 [高德地图API]从零开始学高德JS API(三)覆盖物--标注|折线|多边形|信息窗口|聚合marker|麻点图|图片覆盖物 [高德地图API]从零开始学高德JS API(二)地图控件与插件--测距.圆形编辑器.鼠标工具.地图类型切换.鹰眼鱼骨 [高德地图API]从零开始学高德JS API

Android之仿ele地图定位效果

PS:最近项目要求,希望在选择地址的时候能够仿ele来实现定位效果.因此就去做了一下.不过ele使用高德地图实现的,我是用百度地图实现的.没办法,公司说用百度那就用百度的吧.个人觉得高德应该更加的精准.但也无所谓反正都是地图定位+Poi搜索.都差不多.   1.使用LocationClient核心类实现定位 2.使用GeoCoder实现地理编码和反地理编码 3.使用PoiSearch实现相关的Poi搜索 4.使用SuggestionSearch实现在线建议查询 5.ele定位效果的实现   百度