高德地图Marker和Marker点击事件处理
界面上添加marker的相关操作
因为我做的景区里面有很多的景点,在显示景点类型的情况下就需要显示很多的景点marker,所以就在地图上面添加了景点的marker。下面是添加景点marker的代码。如果想要添加多个marker,循环遍历下面方法即可。
/**
* 往地图上添加marker
*/
public void addMarkersToMap(Context context, LatLng latlng, String title, int icon, int i,boolean isSpot) {
if (mAmap != null) {
View view = View.inflate(context, R.layout.marker_show, null);
TextView textView = (TextView) view.findViewById(R.id.tv_marker_show);
ImageView imageView = (ImageView) view.findViewById(R.id.iv_marker_show);
imageView.setImageResource(icon);
if ("".equals(title) || title == null) {
textView.setVisibility(View.GONE);
} else {
textView.setVisibility(View.VISIBLE);
}
textView.setText(title);
Bitmap bitmap = BitmapUtils.convertViewToBitmap(view);
int radius = DensityUtil.dip2px(context, isSpot ? 225 : 170);
markerOption = new MarkerOptions()
.position(latlng)
.draggable(true)
.title(i + "")
.setInfoWindowOffset(0, radius) // 需要慢慢调试情况
.icon(BitmapDescriptorFactory.fromBitmap(bitmap));
Marker marker = mAmap.addMarker(markerOption);
// 将marker保存 方便后面清除
mAllMarker.add(marker);
// 释放资源
bitmap.recycle();
}
}
因为我的marker比较复杂,不是单纯的一个图片就可以轻松的搞定,所以我这边自定义了view,然后将这个自定义的view打包成bitmap交给Amap。其实也可以不打包成bitmap,高德有api可以直接用view,但是它将view拿过去之后还是转成了图片的格式。
从开始new一个view的到转成bitmap都是属于给当前需要显示的view界面进行赋值,让marker在界面上显示的不一样。
radius:是我这边要显示的infowindow的y轴方向的偏移量,我这边由于UI切图问题和UI测试需要显示的问题导致我这边需要进行向下的偏移。
position:设置当前marker显示在界面的什么位置,即经纬度的传入。
draggable:当前marker是否可以拖动,true 可以拖动。
title:设置marker的主题。如果不设置title也不设置snippet的情况,点击这个marker是不会出现infowindow 我这边设置的position的编号,主要是我要根据marker里面保存的信息来找出当前显示的是哪一个景点的marker,那么在点击marker的时候就可以根据对应的marker显示对应的内容。
setInfoWindowOffset(int x,int y); x是横向的偏移量(正数表示向右偏移,负数表示向左偏移),y是竖向的偏移量(正数表示向下偏移,负数表示向上偏移)。
icon:将上面的view的bitmap传给地图
mAmap.addMarker(markerOption);
将上面对marker的配置添加到地图上,这是地图上面就显示了这个marker。
下面是我这边自定义的marker的样式。
添加marker的点击事件
// marker 点击事件监听
mAmap.setOnMarkerClickListener(mMarkerListener);
备注:一定要设置marker的title或者snippet,否则就算有点击事件,也不会调用getInfoWindow的方法。
点击事件的监听
在点击事件中不管界面有没有显示当前marker的infowindow,再次进行点击都会是显示infowindow。由于infowindow界面默认只显示一个,如果你点击其他的marker就会显示其他的,当前这个就会关闭掉。
/**
* 设置marker的点击事件
*/
AMap.OnMarkerClickListener mMarkerListener = new AMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
if (marker.isInfoWindowShown()) {
marker.hideInfoWindow();
} else {
marker.showInfoWindow();
}
return true; // 返回:true 表示点击marker 后marker 不会移动到地图中心;返回false 表示点击marker 后marker 会自动移动到地图中心
}
};
infowindow的显示
mAmap.setInfoWindowAdapter(mAMapSpotAdapter); // 设置marker点击之后显示的infowindow界面
mAMapSpotAdapter 源代码如下:
AMap.InfoWindowAdapter mAMapSpotAdapter = new AMap.InfoWindowAdapter() {
@Override
public View getInfoWindow(Marker marker) {
if ("".equals(marker.getTitle()) || marker.getTitle() == null) {
return null;
}
View infoContent = getLayoutInflater().inflate(R.layout.marker_click_spot_type, null);
render(marker, infoContent);
return infoContent;
}
@Override
public View getInfoContents(Marker marker) {
if ("".equals(marker.getTitle()) || marker.getTitle() == null) {
return null;
}
View infoContent = getLayoutInflater().inflate(R.layout.marker_click_spot_type, null);
render(marker, infoContent);
return infoContent;
}
private void render(Marker marker, View view) {
// 界面的绘制加数据 当前是景点显示的infowindow 60 是正常情况 225 仅仅对景区景点的设置底部问题 带有箭头的正常情况下65
RelativeLayout rl_all_info = view.findViewById(R.id.rl_all_info);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(rl_all_info.getLayoutParams());
int view_x = DpUtil.dp2px(mContext,200); // infowindow的宽度
int bottom = DpUtil.dp2px(mContext,170); // 正常情况下底部偏移量问题
int padding_view = DpUtil.dp2px(mContext,32)*2; // 正常情况下底部偏移量问题
// 判断当前是上下左右哪一个地方的显示
int type = showInfoWindowSpotLTRB(marker); // 1 marker 在屏幕的左边 2 marker 在屏幕的上面 3 marker 在屏幕的右边 4 marker 在屏幕的底部 5 左上 6 左下 7 右上 8 右下
if (type == 1) { // top // 需要显示在顶部中间位置
lp.setMargins(0,0, 0, 0);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_top_center);
} else if (type == 2 ){ // topleft // 顶部左边 需要显示在顶部最左边位置
lp.setMargins(view_x-padding_view,0, 0, 0);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_top_left);
} else if (type == 3 ){ // topright // 顶部右边 需要显示在顶部最右边位置
lp.setMargins(0,0, view_x-padding_view, 0);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_top_right);
} else if (type == 4 ){ // right // 右边 需要显示在右边中间位置
lp.setMargins(0,0, view_x, bottom/2);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_right_center);
} else if (type == 5 ){ // left // 左边 需要显示在左边中间位置
lp.setMargins(view_x,0, 0, bottom/2);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_left_center);
} else if (type == 6 ){ // leftBotttom // 底部左边 需要显示在底部最左边位置
lp.setMargins(view_x-padding_view,0, 0, bottom);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_bottom_left);
} else if (type == 7 ){ // rightBottom // 底部右边 需要显示在底部最右边位置
lp.setMargins(0,0, view_x-padding_view, bottom);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_bottom_right);
} else if (type == 8 ){ // Bottom // 底部 需要显示在底部中间位置
lp.setMargins(0,0, 0, bottom);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_bottom_center);
} else { // 正常情况下 显示在底部中间位置
lp.setMargins(0,0, 0, bottom);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_bottom_center);
}
// 设置属性
rl_all_info.setLayoutParams(lp);
// 音频播放点击事件
RelativeLayout rlImg = view.findViewById(R.id.rl_img);
// 景点图片显示 圆形图片显示
RoundImageView ivScenicSpotImg = view.findViewById(R.id.iv_scenic_spot_img);
// 播放的图片
ImageView ivPlayImg = view.findViewById(R.id.iv_play_img);
// 景点名称
TextView tvTitle = view.findViewById(R.id.tv_title);
// 景点距离
TextView tvPosition = view.findViewById(R.id.tv_position);
// 解说
ImageView ivListen = view.findViewById(R.id.iv_intro);
// 详请
ImageView ivDetail = view.findViewById(R.id.iv_detail);
int period = Integer.parseInt(marker.getTitle());
ScenicSpotGuideBean.ScenicSpotListBean bean = mInfo.getScenicSpotList().get(period);
tvTitle.setText(bean.getName());
// 获取当前marker的经纬度
LatLng markerLatLng = marker.getPosition();
// 获取当前我的经纬度
LatLng myLatLng = new LatLng(Constants.mLatitude,Constants.mLongitude);
// 计算当前两个经纬度的距离问题
float distance = AMapUtils.calculateLineDistance(markerLatLng, myLatLng);
tvPosition.setText("距离" + AMapUtil.getFriendlyLength((int) distance));
GlideUtil.loadImgRadius(ivScenicSpotImg, 0, bean.getPhotoUrl());
// 点击事件
// 播放点击事件
rlImg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 更新当前界面
}
});
// 播放点击事件
ivPlayImg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 更新当前界面
}
});
// 解说
ivListen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 更新当前界面
}
});
// 详请
ivDetail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
marker.hideInfoWindow(); // 如果想要隐藏当前的infowindow就调用这个api
}
});
}
};
因为我这边的这个比较复杂,你们比较简单的可以不管render()里面的内容,我这边赋值是在render里面去做的赋值处理。
下面是点击之前和点击之后的界面变化。
因为滑动收到了限制。所以就会出现在边界的marker如果正常显示就会有一部分看不到,所以我这边根据当前marker在屏幕上的位置来判断当前marker所处位置是否可以显示正个infowindow的布局,如果会被隐藏一部分就做偏移处理,如marker在界面的右边就,往左偏移一定的值,就可以正常的看到全部的布局。
RelativeLayout rl_all_info = view.findViewById(R.id.rl_all_info);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(rl_all_info.getLayoutParams());
int view_x = DpUtil.dp2px(mContext,200); // infowindow的宽度
int bottom = DpUtil.dp2px(mContext,170); // 正常情况下底部偏移量问题
int padding_view = DpUtil.dp2px(mContext,32)*2; // 正常情况下底部偏移量问题
// 判断当前是上下左右哪一个地方的显示
int type = showInfoWindowSpotLTRB(marker); // 1 marker 在屏幕的左边 2 marker 在屏幕的上面 3 marker 在屏幕的右边 4 marker 在屏幕的底部 5 左上 6 左下 7 右上 8 右下
if (type == 1) { // top // 需要显示在顶部中间位置
lp.setMargins(0,0, 0, 0);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_top_center);
} else if (type == 2 ){ // topleft // 顶部左边 需要显示在顶部最左边位置
lp.setMargins(view_x-padding_view,0, 0, 0);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_top_left);
} else if (type == 3 ){ // topright // 顶部右边 需要显示在顶部最右边位置
lp.setMargins(0,0, view_x-padding_view, 0);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_top_right);
} else if (type == 4 ){ // right // 右边 需要显示在右边中间位置
lp.setMargins(0,0, view_x, bottom/2);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_right_center);
} else if (type == 5 ){ // left // 左边 需要显示在左边中间位置
lp.setMargins(view_x,0, 0, bottom/2);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_left_center);
} else if (type == 6 ){ // leftBotttom // 底部左边 需要显示在底部最左边位置
lp.setMargins(view_x-padding_view,0, 0, bottom);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_bottom_left);
} else if (type == 7 ){ // rightBottom // 底部右边 需要显示在底部最右边位置
lp.setMargins(0,0, view_x-padding_view, bottom);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_bottom_right);
} else if (type == 8 ){ // Bottom // 底部 需要显示在底部中间位置
lp.setMargins(0,0, 0, bottom);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_bottom_center);
} else { // 正常情况下 显示在底部中间位置
lp.setMargins(0,0, 0, bottom);
rl_all_info.setBackgroundResource(R.drawable.marker_spot_bottom_center);
}
// 设置属性
rl_all_info.setLayoutParams(lp);
这一部分的代码是做布局的偏移显示,我分成八种情况分别是:顶部,顶部左边,顶部右边,左边,右边,底部,底部左边,底部右边。如果你也需要做这样的操作,你可以根据你的布局进行详细的偏移。如果要显示在marker的底部,需要在最开始的时候设置marker的setInfoWindowOffset的y方向的偏移量默认显示在底部,不然后面改margin是不能浮动到marker的底部的。
// 获取当前marker的经纬度
LatLng markerLatLng = marker.getPosition();
// 获取当前我的经纬度
LatLng myLatLng = new LatLng(Constants.mLatitude,Constants.mLongitude);
// 计算当前两个经纬度的距离问题
float distance = AMapUtils.calculateLineDistance(markerLatLng, myLatLng);
tvPosition.setText("距离" + AMapUtil.getFriendlyLength((int) distance));
这个是根据我当前的位置和marker的位置计算出我与当前景点的距离。
/**
* 获取infowindow应该显示为位置
* @return type 0 表示不做更改 1 顶部 2 顶部左边 3 顶部右边 4 右边 5 左边 6 底部左边 7 顶部右边 8 底部
*/
private int showInfoWindowSpotLTRB(Marker marker){
Point markerPoint = mAmap.getProjection().toScreenLocation(marker.getPosition()); // 返回一个从地图位置转换来的屏幕位置
int type = 0;
int screen_X = getResources().getDisplayMetrics().widthPixels;
int screen_Y = getResources().getDisplayMetrics().heightPixels;
int marker_x = markerPoint.x;
int marker_y = markerPoint.y;
int view_x = DpUtil.dp2px(mContext,152)/2; // infowindow的宽度
int view_y = DpUtil.dp2px(mContext,108)/2; // infowindow的高度
int top_y = DpUtil.dp2px(mContext,103);
int Bottom_y = DpUtil.dp2px(mContext,135);
if (((screen_X-marker_x) < view_x)){ // 横向判断当前marker位置是否是在右边 表示在右边
// 当前已经在右边了 判断有没有在顶部
if ((screen_Y - marker_y) < (view_y + Bottom_y)){ // 当前在底部 右边底部
type = 7;
} else if ((marker_y-top_y) < view_y){ // 当前在顶部 右边顶部
type = 3;
} else { // 右边
type = 4;
}
} else if (marker_x < view_x){ // 当前在左边
if ((screen_Y - marker_y) < (view_y + Bottom_y)){ // 当前在底部 左边底部
type = 6;
} else if ((marker_y-top_y) < view_y){ // 当前在顶部 左边顶部
type = 2;
} else { // 左边
type = 5;
}
} else if ((marker_y-top_y) < view_y){ // 当前在顶部 顶部
type = 1;
} else if ((screen_Y - marker_y) < (view_y + Bottom_y)){ // 当前在底部 底部
type = 8;
} else { //不做操作
type = 0;
}
return type;
}
上面这个方法是计算当前marker在屏幕的什么位置。
mAmap.getProjection().toScreenLocation(marker.getPosition());
这个根据marker获取屏幕位置时高德地图官方提供的。