我们使用百度地图的时候,点击地图上的Marker,会弹出一个该地点详细信息的窗口,如下左图所示,有时候,我们希望自己定义这个弹出窗口的内容,或者,干脆用自己的数据来构造这样的弹出窗口,但是,在百度地图最新的Android SDK中,没有方便操作这种弹出窗口的类,虽然有一个PopupOverlay,但是它只支持将弹出内容转化为不多于三个Bitmap,如果这个弹出窗口里想有按钮来响应点击事件,用这个就不能满足要求了,于是,看了一遍百度地图覆盖物的API,我决定用自定义View的方法来实现类似的效果,先贴一下大体效果图,如下右图:

      

  基本原理就是用ItemizedOverlay来添加附加物,在OnTap方法中向MapView上添加一个自定义的View(如果已存在就直接设为可见),下面具体来介绍我的实现方法:

  一、自定义覆盖物类:MyPopupOverlay,这个类是最关键的一个类ItemizedOverlay,用于设置Marker,并定义Marker的点击事件,弹出窗口,至于弹出窗口的内容,则通过定义Listener,放到Activity中去构造。如果没有特殊需求,这个类不需要做什么改动。代码如下,popupLinear这个对象,就是加到地图上的自定义View:

 public class MyPopupOverlay extends ItemizedOverlay<OverlayItem> {

     private Context context = null;
// 这是弹出窗口, 包括内容部分还有下面那个小三角
private LinearLayout popupLinear = null;
// 这是弹出窗口的内容部分
private View popupView = null;
private MapView mapView = null;
private Projection projection = null; // 这是弹出窗口内容部分使用的layoutId,在Activity中设置
private int layoutId = 0;
// 是否使用百度带有A-J字样的Marker
private boolean useDefaultMarker = false;
private int[] defaultMarkerIds = { R.drawable.icon_marka,
R.drawable.icon_markb, R.drawable.icon_markc,
R.drawable.icon_markd, R.drawable.icon_marke,
R.drawable.icon_markf, R.drawable.icon_markg,
R.drawable.icon_markh, R.drawable.icon_marki,
R.drawable.icon_markj, }; // 这个Listener用于在Marker被点击时让Activity填充PopupView的内容
private OnTapListener onTapListener = null; public MyPopupOverlay(Context context, Drawable marker, MapView mMapView) {
super(marker, mMapView);
this.context = context;
this.popupLinear = new LinearLayout(context);
this.mapView = mMapView;
popupLinear.setOrientation(LinearLayout.VERTICAL);
popupLinear.setVisibility(View.GONE);
projection = mapView.getProjection();
} @Override
public boolean onTap(GeoPoint pt, MapView mMapView) {
// 点击窗口以外的区域时,当前窗口关闭
if (popupLinear != null && popupLinear.getVisibility() == View.VISIBLE) {
LayoutParams lp = (LayoutParams) popupLinear.getLayoutParams();
Point tapP = new Point();
projection.toPixels(pt, tapP);
Point popP = new Point();
projection.toPixels(lp.point, popP);
int xMin = popP.x - lp.width / 2 + lp.x;
int yMin = popP.y - lp.height + lp.y;
int xMax = popP.x + lp.width / 2 + lp.x;
int yMax = popP.y + lp.y;
if (tapP.x < xMin || tapP.y < yMin || tapP.x > xMax
|| tapP.y > yMax)
popupLinear.setVisibility(View.GONE);
}
return false;
} @Override
protected boolean onTap(int i) {
// 点击Marker时,该Marker滑动到地图中央偏下的位置,并显示Popup窗口
OverlayItem item = getItem(i);
if (popupView == null) {
// 如果popupView还没有创建,则构造popupLinear
if (!createPopupView()){
return true;
}
}
if (onTapListener == null)
return true;
popupLinear.setVisibility(View.VISIBLE);
onTapListener.onTap(i, popupView); popupLinear.measure(0, 0);
int viewWidth = popupLinear.getMeasuredWidth();
int viewHeight = popupLinear.getMeasuredHeight(); LayoutParams layoutParams = new LayoutParams(viewWidth, viewHeight,
item.getPoint(), 0, -60, LayoutParams.BOTTOM_CENTER);
layoutParams.mode = LayoutParams.MODE_MAP; popupLinear.setLayoutParams(layoutParams);
Point p = new Point();
projection.toPixels(item.getPoint(), p);
p.y = p.y - viewHeight / 2;
GeoPoint point = projection.fromPixels(p.x, p.y); mapView.getController().animateTo(point);
return true;
} private boolean createPopupView() {
// TODO Auto-generated method stub
if (layoutId == 0)
return false;
popupView = LayoutInflater.from(context).inflate(layoutId, null);
popupView.setBackgroundResource(R.drawable.popupborder);
ImageView dialogStyle = new ImageView(context);
dialogStyle.setImageDrawable(context.getResources().getDrawable(
R.drawable.iw_tail));
popupLinear.addView(popupView);
android.widget.LinearLayout.LayoutParams lp = new android.widget.LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
lp.topMargin = -2;
lp.leftMargin = 60;
popupLinear.addView(dialogStyle, lp);
mapView.addView(popupLinear);
return true;
} @Override
public void addItem(List<OverlayItem> items) {
// TODO Auto-generated method stub
int startIndex = getAllItem().size();
for (OverlayItem item : items){
if (startIndex >= defaultMarkerIds.length)
startIndex = defaultMarkerIds.length - 1;
if (useDefaultMarker && item.getMarker() == null){
item.setMarker(context.getResources().getDrawable(
defaultMarkerIds[startIndex++]));
}
}
super.addItem(items);
} @Override
public void addItem(OverlayItem item) {
// TODO Auto-generated method stub
// 重载这两个addItem方法,主要用于设置自己默认的Marker
int index = getAllItem().size();
if (index >= defaultMarkerIds.length)
index = defaultMarkerIds.length - 1;
if (useDefaultMarker && item.getMarker() == null){
item.setMarker(context.getResources().getDrawable(
defaultMarkerIds[getAllItem().size()]));
}
super.addItem(item);
} public void setLayoutId(int layoutId) {
this.layoutId = layoutId;
} public void setUseDefaultMarker(boolean useDefaultMarker) {
this.useDefaultMarker = useDefaultMarker;
} public void setOnTapListener(OnTapListener onTapListener) {
this.onTapListener = onTapListener;
} public interface OnTapListener {
public void onTap(int index, View popupView);
}
}

  二、MainActivity,这是主界面,用来显示地图,创建MyPopupOverlay对象,在使用我写的MyPopupOverlay这个类时,需要遵循以下步骤:

  1. 创建MyPopupOverlay对象,构造函数为public MyPopupOverlay(Context context, Drawable marker, MapView mMapView),四个参数分别为当前的上下文、通用的Marker(这是ItemizedOverlay需要的,当不设置Marker时的默认Marker)以及百度地图对象。
  2. 设置自定义的弹出窗口内容的布局文件ID,使用的方法为public void setLayoutId(int layoutId)。
  3. 设置是使用自定义的Marker,还是预先写好的带有A-J字样的百度地图原装Marker,使用的方法为public void setUseDefaultMarker(boolean useDefaultMarker),只有当这个值为true且没有调用OverlayItem的setMarker方法为特定点设置Marker时,才使用原装Marker。
  4. 创建Marker所在的点,即分别创建一个个OverlayItem,然后调用public void addItem(OverlayItem item)或public void addItem(List<OverlayItem> items)方法来把这些OverlayItem添加到自定义的附加层上去。
  5. 为MyPopupOverlay对象添加onTap事件,当Marker被点击时,填充弹出窗口中的内容(也就是第2条中layoutId布局中的内容),设置方法为public void setOnTapListener(OnTapListener onTapListener),OnTapListener是定义在MyPopupOverlay中的接口,实现这个接口需要覆写public void onTap(int index, View popupView)方法,其中,index表示被点击的Marker(确切地说是OverlayItem)的索引,popupView是使用layoutId这个布局的View,也就是弹出窗口除了下面的小三角之外的部分。
  6. 把这个MyPopupOverlay对象添加到地图上去:mMapView.getOverlays().add(myOverlay);mMapView.refresh();

  下面是我的代码(MainActivity):

public class MainActivity extends Activity {

    private BMapManager mBMapMan = null;
private MapView mMapView = null;
private String keyString = "这里填入申请的KEY"; @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState); mBMapMan = new BMapManager(getApplication());
mBMapMan.init(keyString, new MKGeneralHandler(MainActivity.this));  //MKGeralHandler是一个实现MKGeneralListener接口的类,详见百度的文档 setContentView(R.layout.activity_main);  // activity_main.xml中就是百度地图官方文档提供的LinearLayout下面放一个MapView的布局
mMapView = (MapView) findViewById(R.id.bmapsView);  // 获取地图MapView对象
mMapView.setBuiltInZoomControls(true);
final MapController mMapController = mMapView.getController();
mMapController.setZoom(16); GeoPoint p1 = new GeoPoint(39113458, 117183652);  // 天大正门的坐标
GeoPoint p2 = new GeoPoint(39117258, 117178252);  // 天大大活的坐标
mMapController.animateTo(p1); //声明MyPopupOverlay对象
MyPopupOverlay myOverlay = new MyPopupOverlay(
MainActivity.this,
getResources().getDrawable(R.drawable.icon_gcoding),
mMapView);                    // 这是第1步,创建MyPopupOverlay对象
myOverlay.setLayoutId(R.layout.popup_content);    // 这是第2步,设置弹出窗口的布局文件
myOverlay.setUseDefaultMarker(true);          // 这是第3步,设置是否使用A-J的Marker OverlayItem item1 = new OverlayItem(p1, "", "");
OverlayItem item2 = new OverlayItem(p2, "", ""); List<OverlayItem> items = new ArrayList<OverlayItem>();
items.add(item1);
items.add(item2); myOverlay.addItem(items);          // 这是第4步,向MyPopupOverlay中依次添加OverlayItem对象,或存到链表中一次性添加
// myOverlay.addItem(item2); final List<MapPopupItem> mItems = new ArrayList<MapPopupItem>();  // 这是暂时自己造的model对象,存储显示的数据
MapPopupItem mItem = new MapPopupItem();
mItem.setTitle("天津大学");
// ...... 这里依次添加了地址、电话、标签、图片等信息
mItems.add(mItem);
mItem = new MapPopupItem();
mItem.setTitle("天津大学大学生活动中心");
// ...... 同样添加第二个点的地址、电话、标签、图片信息
mItems.add(mItem); myOverlay.setOnTapListener(new OnTapListener() { @Override
public void onTap(int index, View popupView) {    // 这是第5步,设置监听器,为popupView填充数据
// TODO Auto-generated method stub
MapPopupItem mItem = mItems.get(index);      // 这是存储model数据的数组,根据被点击的点的index获取具体对象 TextView shopName = (TextView) popupView.findViewById(R.id.name);
// ...... 依次获得视图中的各个控件(地址、电话、标签、图片等) shopName.setText(mItem.getTitle());
// ...... 依次为这些控件赋上值(地址、电话、标签、图片等信息)
}
}); mMapView.getOverlays().add(myOverlay);           // 最后一步,添加覆盖物层
mMapView.refresh();
} @Override
protected void onDestroy() {
mMapView.destroy();
if (mBMapMan != null) {
mBMapMan.destroy();
mBMapMan = null;
}
super.onDestroy();
} @Override
protected void onPause() {
mMapView.onPause();
if (mBMapMan != null) {
mBMapMan.stop();
}
super.onPause();
} @Override
protected void onResume() {
mMapView.onResume();
if (mBMapMan != null) {
mBMapMan.start();
} super.onResume();
}
}

  这就是主要的思路和代码了,因为代码文件、资源文件弄得比较多,不大容易贴出来全部能直接运行的代码,而且布局文件里控件太多也不容易理解,就这么写了,如果大家有什么更好的方法,或者有什么好的建议,欢迎讨论和指正。

  注:为了说明问题,主类中我简化了很多东西,而且有些图片找起来也挺麻烦,把源代码附在这里供大家参考,运行前需要在MainActivity中修改百度地图的Key。

  下载链接

【Android】百度地图自定义弹出窗口的更多相关文章

  1. ArcGIS API for Silverlight 当DataGrid选中项时,地图聚焦弹出窗口,并可以播放音频文件

    原文:ArcGIS API for Silverlight 当DataGrid选中项时,地图聚焦弹出窗口,并可以播放音频文件 先看效果图,然后上代码: <UserControl x:Class= ...

  2. CSS3/jQuery自定义弹出窗口

    简单演示一下,精简了演示效果和css样式文件,更利于在项目中的实际应用 引入style.css   index.js <!DOCTYPE HTML PUBLIC "-//W3C//DT ...

  3. 读《深入理解Windows Phone 8.1 UI控件编程》1.4.3 框架的应用示例:自定义弹出窗口有感

    前些天买了园子里林政老师的两本 WP8.1 的书籍.毕竟想要学得深入的话,还是得弄本书跟着前辈走的. 今天读到 1.4.3 节——框架的应用示例:自定义弹出窗口这一小节.总的来说,就是弄一个像 Mes ...

  4. html5+css3+javascript 自定义弹出窗口

    效果图: 源码: 1.demo.jsp <%@ page contentType="text/html;charset=UTF-8" language="java& ...

  5. 百度地图 layer弹出地图 获取坐标

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. html里面自定义弹出窗口

    网页上默认的提示框或对话框一般比较丑,可以利用div遮盖层来自定义对话框 1.定义一个按钮或者链接(项目里面是通过点击一个图片) <img src="images/zz.gif&quo ...

  7. Android 定时重复启动弹出窗口。

    本来想着用handlerpostdelay就可以实现,没想到演示后关闭应用居然报错. 后来想到是没有了activity. ((Activity)context).isFinishing() 可以传入c ...

  8. UpdatePanel无法直接弹出窗口的解决

    UpdatePanel无法直接弹出窗口的解决 2010-06-20  来自:博客园  字体大小:[大 中 小] 摘要:本文介绍一些UpdatePanel无法直接弹出窗口的解决方法 ///<sum ...

  9. android自定义弹出框样式实现

    前言: 做项目时,感觉Android自带的弹出框样式比较丑,很多应用都是自己做的弹出框,这里也试着自己做了一个. 废话不说先上图片: 实现机制 1.先自定义一个弹出框的样式 2.自己实现CustomD ...

随机推荐

  1. UVALive 3664:Guess(贪心 Grade E)

    vj题目链接 题意: 有n (n<16345)个人,每个人有三个数(小于1000且最多两位小数点),表示答对对应题的得分.规定总分越高的人rank越高.总分相同,id小的rank高.现在知道ra ...

  2. 关于main函数的参数

    #include <stdio.h> int main(int argc, char const *argv[]) { int i; for ( i = 0; i < argc; i ...

  3. 【转载】SQL Server XML Path

    FOR XML PATH 有的人可能知道有的人可能不知道,其实它就是将查询结果集以XML形式展现,有了它我们可以简化我们的查询语句实现一些以前可能需要借助函数活存储过程来完成的工作.那么以一个实例为主 ...

  4. 怎样在action中获得值栈ValueStack中的值

    1,实现RequestAware接口 //模拟对象    User model=new User();    user.setName=“lisi”;2,ValueStack value=(Value ...

  5. MapReduce1 工作机制

    本文转自:Hadoop MapReduce 工作机制 工作流程 作业配置 作业提交 作业初始化 作业分配 作业执行 进度和状态更新 作业完成 错误处理 作业调度 shule(mapreduce核心)和 ...

  6. visual studio 2010 调试

    非startup project网站 通过attach to process  添加进程w3wp可以实现断点调试 若有多个,可以在iis中添加应用程序池,然后在网站的高级设置里设置应用程序池里,选择对 ...

  7. Source tree配置gitlab

    1.打开控制台:ssh-keygen -t rsa -C "GIT上的账号邮箱" 2.回车 3.输入密码(git上的账号密码) 4.确认密码 5.输入命令 cd .ssh 6.输入 ...

  8. svm中 C 和sigma对街宽和分隔平面的影响

    C越大 街越窄,可能引发过拟合,对于噪声的惩罚力度加大. sigma越小,高斯分布长得又高又瘦, 会造成只会作用于支持向量样本附近,对于未知样本分类效果很差,存在训练准确率可以很高,

  9. sencha toucha获取 constructor中的数据

    config:{ tmp:null }, constructor : function(conf) { this.config.tmp=conf; } 添加配置属性,然后直接用 this.config ...

  10. nginx configure 错误记录

    1.the HTTP rewrite module requires the PCRE library. ./configure: error: the HTTP rewrite module req ...