下面给大家看一个效果图:
需求描述:要求进入地图页面即显示popoverlay气泡和具体的描述内容,也就是图片中的圆形头像+地址信息,一般情况下,只需要显示坐标点的,比如“A”,“B”等。只有点击的时候才会弹出具体的描述内容。使用SDK:百度地图sdkV2.3.5问题:这种比较复杂的气泡和弹窗,本人也是第一次见到,尤其是还支持圆形头像,由于sdk的demo中提供的示例比较简单,做到如上这些,可是费了不少脑细胞呀!好了,不多说,下面贴出具体的代码://============================== 1、首先是需要创建覆盖物对象-也就是图上面的圆形头像和名称
需求描述:要求进入地图页面即显示popoverlay气泡和具体的描述内容,也就是图片中的圆形头像+地址信息,一般情况下,只需要显示坐标点的,比如“A”,“B”等。只有点击的时候才会弹出具体的描述内容。- <font face="微软雅黑" size="3">/** 创建单点覆盖物
- * createOverlayItem
- * @throws
- */
- @SuppressWarnings("deprecation")
- private void createOverlayItem(Device oneDevice){
- if(oneDevice!=null && oneDevice.getGeo()!=null){
- //这是该覆盖物的坐标--一般来源于服务端
- Double[] geo = oneDevice.getGeo();
- // 为该坐标点添加覆盖物-通过点击marker的方式弹出pop
- GeoPoint stop = new GeoPoint((int) (geo[1] * 1E6),(int) (geo[0] * 1E6));
- //将WGS坐标转化为百度坐标
- GeoPoint g = CoordinateConvert.fromWgs84ToBaidu(stop);
- //实例化该覆盖物--注意该方法的构造参数
- OverlayItem item1 = new OverlayItem(g, oneDevice.getAddress()+"/"+oneDevice.getDirection()+"/"+oneDevice.getSpeed(),""+oneDevice.getCreate_time());
- //这就是头像的base64格式的字符串,一般来源于服务端
- String pic = oneDevice.getPic();
- </font> <font face="微软雅黑" size="3">
- //设置overlay图标-采用view转化为bitmap的方式
- //--这是最关键的地方,因为sdk本身不支持直接显示复杂View,所以需要用另外的方式创建,幸好android提供将View转换为bitmap的方法。
- View view = mLayoutInflater.inflate(R.layout.custom_foot_marker, null);
- TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
- ImageView img_pic = (ImageView) view.findViewById(R.id.img_pic);
- Bitmap orginBitmap = null;
- if (pic != null && !pic.equals("")) {
- orginBitmap =BMapUtil.getBitmap(pic);
- }else{
- orginBitmap= BitmapFactory.decodeResource(getResources(), R.drawable.qp_head);
- }
- //圆形头像
- Bitmap roundBitmap= BMapUtil.toRoundBitmap(orginBitmap);
- img_pic.setImageBitmap(roundBitmap);
- tv_name.setText(oneDevice.getName());
- </font> <font face="微软雅黑" size="3">
- //这就是将View转换为bitmap-并设置成marker
- Bitmap bitmap = BMapUtil.getBitmapFromView(view);
- item1.setMarker(new BitmapDrawable(bitmap));
- // mOverlay-----这是覆盖物集合--所有的覆盖物的创建都是由他支配的
- mOverlay.onCenter(item1);
- //添加该覆盖物到集合中
- mOverlay.addItem(item1);
- //设置地图中心坐标
- mMapView.getController().setCenter(g);
- // //移动地图到起点
- mMapView.getController().animateTo(g);
- //回收资源
- if(orginBitmap!=null&& !orginBitmap.isRecycled()){
- orginBitmap.recycle();
- }
- if(roundBitmap!=null&& !roundBitmap.isRecycled()){
- roundBitmap.recycle();
- }
- if(bitmap!=null&& !bitmap.isRecycled()){
- bitmap.recycle();
- }
- }else{
- ShowToast("暂无数据");
- }
- }
- </font>
上面的代码中用到的方法也一并贴出:
- <font face="微软雅黑" size="3">//这是最重要的方法,因为百度默认setMarker的参数只能是Drawable类型,因此需要进行一步转化。
- /**将View转换为Bitmap
- * @param view
- * @return
- */
- public static Bitmap getBitmapFromView(View view) {
- view.destroyDrawingCache();
- view.measure(View.MeasureSpec.makeMeasureSpec(0,
- View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
- .makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
- view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
- view.setDrawingCacheEnabled(true);
- Bitmap bitmap = view.getDrawingCache(true);
- return bitmap;
- }
-
- /**
- * 根据base64字符串获取Bitmap位图 getBitmap
- * @throws
- */
- public static Bitmap getBitmap(String imgBase64Str) {
- try {
- byte[] bitmapArray;
- bitmapArray = Base64.decode(imgBase64Str, Base64.DEFAULT);
- return BitmapFactory.decodeByteArray(bitmapArray, 0,
- bitmapArray.length);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- /**
- * 转换图片成圆形
- * @param bitmap
- * 传入Bitmap对象
- * @return
- */
- public static Bitmap toRoundBitmap(Bitmap bitmap) {
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- float roundPx;
- float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
- if (width <= height) {
- roundPx = width / 2;
-
- left = 0;
- top = 0;
- right = width;
- bottom = width;
-
- height = width;
-
- dst_left = 0;
- dst_top = 0;
- dst_right = width;
- dst_bottom = width;
- } else {
- roundPx = height / 2;
-
- float clip = (width - height) / 2;
-
- left = clip;
- right = width - clip;
- top = 0;
- bottom = height;
- width = height;
-
- dst_left = 0;
- dst_top = 0;
- dst_right = height;
- dst_bottom = height;
- }
-
- Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
-
- final Paint paint = new Paint();
- final Rect src = new Rect((int) left, (int) top, (int) right,
- (int) bottom);
- final Rect dst = new Rect((int) dst_left, (int) dst_top,
- (int) dst_right, (int) dst_bottom);
- final RectF rectF = new RectF(dst);
-
- paint.setAntiAlias(true);// 设置画笔无锯齿
-
- canvas.drawARGB(0, 0, 0, 0); // 填充整个Canvas
-
- // 以下有两种方法画圆,drawRounRect和drawCircle
- canvas.drawRoundRect(rectF, roundPx, roundPx, paint);// 画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。
- // canvas.drawCircle(roundPx, roundPx, roundPx, paint);
-
- paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));// 设置两张图片相交时的模式
- canvas.drawBitmap(bitmap, src, dst, paint); // 以Mode.SRC_IN模式合并bitmap和已经draw了的Circle
-
- return output;
- }
- </font>
- <font face="微软雅黑" size="3">[mw_shl_code=java,true]<?xml version="1.0" encoding="utf-8"?>
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" >
-
- <RelativeLayout
- android:layout_width="65dp"
- android:layout_height="80dp"
- android:background="@drawable/marker_foot" >
-
- <ImageView
- android:id="@+id/img_pic"
- android:layout_width="55dp"
- android:layout_height="55dp"
- android:layout_marginTop="5dp"
- android:layout_centerHorizontal="true"
- android:scaleType="fitCenter"
- android:src="@drawable/qp_head"
- />
- </RelativeLayout>
-
- <TextView
- android:id="@+id/tv_name"
- android:layout_width="45dp"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_horizontal"
- android:layout_marginBottom="20dp"
- android:background="@drawable/marker_text_bg"
- android:gravity="center_horizontal"
- android:text="smile"
- android:textColor="@color/color_white_text"
- android:textSize="10sp" />
-
- </FrameLayout>[/mw_shl_code]</font>
- <font face="微软雅黑" size="3">private View popupInfo = null;
- TextView tv_time;
- TextView tv_address;
- TextView tv_rate;//方向和速度
- PopupOverlay pop;
-
- private void initPop() {
- /**
- * 向地图添加自定义View.
- */
- View viewCache = mLayoutInflater.inflate(R.layout.custom_replay_window, null);
- popupInfo = (View) viewCache.findViewById(R.id.layout_window);
- tv_address = (TextView) viewCache.findViewById(R.id.tv_address);
- tv_rate = (TextView) viewCache.findViewById(R.id.tv_rate);
- tv_time = (TextView) viewCache.findViewById(R.id.tv_time);
-
- /**
- * 创建一个popupoverlay
- */
- PopupClickListener popListener = new PopupClickListener() {
- @Override
- public void onClickedPopup(int index) {
- action2page(currentIndex);
- }
- };
- pop = new PopupOverlay(mMapView, popListener);
- }
- </font>
- <font face="微软雅黑" size="3">[mw_shl_code=java,true]<?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/layout_window"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/pop_replay"
- android:orientation="vertical"
- android:padding="5dp" >
-
- <TextView
- android:id="@+id/tv_address"
- style="@style/Smile.TextView._Black"
- android:textSize="15sp"
- android:layout_gravity="center_vertical"/>
- </font> <font face="微软雅黑" size="3">
- <TextView
- android:id="@+id/tv_rate"
- style="@style/Smile.TextView._Orange"
- android:textSize="15sp"
- android:layout_gravity="center_vertical"/>
-
- <TextView
- android:id="@+id/tv_time"
- style="@style/Smile.TextView._Gray"
- android:layout_marginBottom="15dp"
- android:textSize="14sp"
- android:layout_gravity="center_vertical" />
-
- </LinearLayout>
- [/mw_shl_code]
-
- //==============================</font>
- <font face="微软雅黑" size="3">/** ItemizedOverlay<OverlayItem> 包含了一个OverlayItem列表,相当于一组分条的Overlay,通过继承此类,将一组兴趣点显示在地图上
- */
- public class MyOverlay extends ItemizedOverlay<OverlayItem> {
-
- public MyOverlay(Drawable defaultMarker, MapView mapView) {
- super(defaultMarker, mapView);
- }
-
- @Override
- public boolean onTap(int index) {//通过点击弹出pop
- action2page(index);
- return true;
- }
-
- @Override
- public boolean onTap(GeoPoint pt, MapView mMapView) {
- return false;
- }
- </font> <font face="微软雅黑" size="3">
- /** 设置其中心偏移量
- * onCenter
- * @Title: onCenter
- * @throws
- */
- public boolean onCenter(OverlayItem item) {
- item.setAnchor(0.5f, 1f);
- return true;
- }
- }
-
-
- //==============================</font>
- <font face="微软雅黑" size="3">/** 直接显示pop
- * showPopOverlay
- * @Title: showPopOverlay
- * @throws
- */
- public void showPopOverlay(int index){
- currentIndex = index;
- if(mOverlay!=null && mOverlay.getItem(index)!=null){
- OverlayItem item = mOverlay.getItem(index);
- String string = mOverlay.getItem(index).getTitle();//地址/方向/速度
- String[] s =string.split("/");
- String address = s[0];
- String direction = s[1];
- String speed = s[2];
- String t = mOverlay.getItem(index).getSnippet();//最后更新时间
- if (tv_address != null) {
- tv_address.setText(address);
- } else {
- tv_address.setText("");
- }
- if (tv_rate != null) {
- tv_rate.setText("方向:"+direction+" 速度:"+speed);
- } else {
- tv_rate.setText("");
- }
- long time = Long.parseLong(t);
- if (tv_time != null) {
- tv_time.setText("最后更新时间:"+TimeUtil.longToString(time * 1000,TimeUtil.FORMAT_DATE1_TIME));
- } else {
- tv_time.setText("");
- }
- Bitmap[] bitMaps = {BMapUtil.getBitmapFromView(popupInfo) };
- pop.showPopup(bitMaps, item.getPoint(), BMapUtil.dip2px(this.getActivity(), 75));
- }
- }
-
-
- //==============================</font>
吐槽:
还要和大家说一点的就是:
一般,我们服务端给我们的坐标数据类型有可能并不是百度或者高德地图能够显示准确的坐标类型,因为基本上每个地图使用的坐标类型都不太一致,这也就是为什么服务端传过来的数据在地图上显示和真实位置有偏移,这时候就需要用到坐标转换了,百度地图提供了专门的坐标转换方法,能够很方便的转换:CoordinateConvert.fromWgs84ToBaidu(stop);
但是,坑爹的高德地图,却并未提供api给我们,也许有,可能我未发现,因为我在高德地图论坛里面没发现有人分享出来,问了管理,说要发风邮件给他们技术人员,然后才能提供隐藏api,但是等了快一周时间,毛都没有,最后实在受不,就只能将github上面有人写的关于object-c方面的将WGS84坐标转换为高德地图坐标的方法转成java方法。下篇帖子会分享出代码的!
Ps:
全部代码已经贴出,我的这些代码是可以直接拿来用的,我看网上很多人分享的一些东西拿过来之后实用性不大,不能真正运用到项目中,未考虑与服务端的交互等因素,我以上说的这些基本上是把我在做项目过程中遇见的问题还原罢了,也许有些人会说这不利于新手学习,直接拿来用导致一些人不大愿意思考,我想既然愿意分享出来,就应该尽量详细,并尽量考虑到与服务端交互,减少后来者出错的概率,至于思不思考就看个人啦!