本文是高德地图系列的最后一文,因为光是写这几篇博文,就花去了我一整天时间,什么也没做,甚至饭也没吃,做了一整天,现在身体已经空荡荡了,而且最近一段时间工作很忙,每天都很累,所以打算多去锻炼身体。有任何疑问也可以留言。
最终效果:生成marker并显示详情
由于不能上传2M以上的截图,只录了重点,废话不多讲,速度上车。
Marker表示为地图上的一个点,一个Point,我们可以在这个Marker做各种各样的操作包括,拖动,获取地理坐标,反编译地址等等。现在,我们要将Marker添加到地图上,并达到图上的效果。
在官方论坛上看到很多人不知道如何实现多InfoWindow的方法,基于此,我决定写一篇关于实现多Infowindow的曲线救国的方式,按照我这篇文章,我相信你也能做出这种效果。
ok以我的项目为例,需求是在地图显示一种设备的详情,可以看到设备ID,类型,次数,经纬度,反地理位置,时间,电量等等。下面是我的实体类BeanEvent 。
private Integer id;
private String deviceId;
private String simId;
private String eventId;
private String eventType;
private String eventCount;
private String latitude;
private String longitude;
private String eventTime;
private String actTime;
private String battery;
private String eventIp;
---此处省略get set方法
// 假数据
public static List<BeanEvent> getEvent()
{
List<BeanEvent> list = new ArrayList<>();
BeanEvent be1 = new BeanEvent();
be1.setDeviceId("devid--1111111");
be1.setSimId("simid--1111111");
be1.setEventId("event_1111111");
be1.setEventType("type-1111111");
be1.setEventCount("count_1111111");
be1.setLatitude("39.92746");
be1.setLongitude("116.396481");
be1.setEventTime("20170911");
be1.setBattery("1111111");
be1.setEventIp("1111111");
BeanEvent be2 = new BeanEvent();
be2.setDeviceId("devid--222222");
be2.setSimId("simid--22222");
be2.setEventId("event_22222");
be2.setEventType("type-22222");
be2.setEventCount("count_22222");
be2.setLatitude("39.93746");
be2.setLongitude("116.398681");
be2.setEventTime("20170911");
be2.setBattery("1111111");
be2.setEventIp("1111111");
BeanEvent be3 = new BeanEvent();
be3.setDeviceId("devid--33333");
be3.setSimId("simid--3333");
be3.setEventId("event_33333");
be3.setEventType("type-33333");
be3.setEventCount("count_33333");
be3.setLatitude("39.92956");
be3.setLongitude("116.399541");
be3.setEventTime("20170911");
be3.setBattery("1111111");
be3.setEventIp("1111111");
list.add(be1);
list.add(be2);
list.add(be3);
return list;
}
至此,数据源被模拟出来了。
官方的添加marker的方法
/**
* 在地图上添加marker
*/
private void addMarkersToMap() {
markerOption = new MarkerOptions().icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_AZURE))
.position(latlng)
.title("标题")
.snippet("详细信息")
.draggable(true);
marker = aMap.addMarker(markerOption);
marker.showInfoWindow();
}
添加InfoWindow的代码我就不演示了,太浪费大家时间,我会直接告诉你,官方代码里的Marker的InfoWindow不支持多个同时显示,当设置了多个Marker的Infowindow时,只显示最后一个,这时该怎么办呢?
看看我是如何将他们显示出来的
***初始化。。。
addMarkersToMap(getEvent()); // 添加点
/**
* 在地图上添加marker
*/
private void addMarkersToMap(List<BeanEvent> eventList)
{
for (int i = 0; i < eventList.size(); i++)
{
BeanEvent be = eventList.get(i);
markerOption = new MarkerOptions().icon(BitmapDescriptorFactory
.fromView(getBitmapView(mActivity, be))).draggable(false);
markerOption.position(new LatLng(Double.parseDouble(be.getLatitude()), Double.parseDouble(be.getLongitude())));
marker = aMap.addMarker(markerOption);
marker.setObject(be);
markerList.add(marker);
}
}
其中,BitmapDescriptorFactory
.fromView(getBitmapView(mActivity, be))方法将返回一个View对象作为Marker的Icon,因此,这里,我们可以借助这个方法,实现将Marker与Infowindow结合在一起,即,看起来是marker和Infowindow,实际上,它们整体就是一个Marker。因为marker是多支持多个显示的,所以这样的思路是没有问题的。
其方法是这样写的:
public static View getBitmapView(Context context, BeanEvent be)
{
LayoutInflater factory = LayoutInflater.from(context);
View view = factory.inflate(R.layout.item_marker_title, null);
TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
TextView tv_snippet = (TextView) view.findViewById(R.id.tv_snippet);
tv_title.setText("SIM卡号:" + be.getSimId());
tv_snippet.setText("公章ID:" + be.getDeviceId());
return view;
}
该方法将传入的对象赋在对应的控件上,然后作为View返回。此时的预览图为
上图就是我们最终作为marker的icon的图,届时,只需要传入虚拟数据或真实数据,即可显示出来。
此时多个InfoWindow就实现了。接下来,我们将实现点击marker弹出详情。
我们先来看一下布局结构:
<RelativeLayout
android:id="@+id/rl_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_margin="2dp"
android:animateLayoutChanges="true"
android:background="@drawable/bg_seal_info"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical"
android:showDividers="middle"
android:visibility="gone"
app:divider="@drawable/iv_devider">
<TextView
android:id="@+id/tv_close"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:background="@mipmap/ic_seal"/>
<LinearLayout
android:id="@+id/ll_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp">
<TextView
android:id="@+id/tv_sim"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="卡号:"
android:textSize="14sp"/>
<TextView
android:id="@+id/tv_count"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="次数:"
android:textSize="14sp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ll_1"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp">
<TextView
android:id="@+id/tv_sealNo"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="公章号:"
android:textSize="14sp"/>
<TextView
android:id="@+id/tv_type"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="事件类型:"
android:textSize="14sp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ll_2"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp">
<TextView
android:id="@+id/tv_latitude"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="北纬:"
android:textSize="14sp"/>
<TextView
android:id="@+id/tv_longitude"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="东经:"
android:textSize="14sp"/>
</LinearLayout>
<TextView
android:id="@+id/tv_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ll_3"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp"
android:text="地址:"
android:textSize="14sp"/>
<LinearLayout
android:id="@+id/ll_4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_address"
android:orientation="horizontal"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp">
<TextView
android:id="@+id/tv_battery"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="电量:"
android:textSize="14sp"/>
<TextView
android:id="@+id/tv_ip"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="IP:"
android:textSize="14sp"/>
</LinearLayout>
</RelativeLayout>
效果为:
由于详情中,用到了反地理编译,即已知经纬度获取地理位置。
所以我们在Activity中实现以下几个接口:
implements GeocodeSearch.OnGeocodeSearchListener, AMap.OnMarkerClickListener
同时,将刚刚的布局隐藏掉,作为一个适配器使用:
public void setInfo(final RelativeLayout rl_layout, Marker marker, RegeocodeResult result)
{
ViewHolder viewHolder = null;
if (rl_layout.getTag() == null)
{
viewHolder = new ViewHolder();
viewHolder.tv_close = (TextView) rl_layout.findViewById(R.id.tv_close);
viewHolder.tv_sim = (TextView) rl_layout.findViewById(R.id.tv_sim);
viewHolder.tv_count = (TextView) rl_layout.findViewById(R.id.tv_count);
viewHolder.tv_sealNo = (TextView) rl_layout.findViewById(R.id.tv_sealNo);
viewHolder.tv_address = (TextView) rl_layout.findViewById(R.id.tv_address);
viewHolder.tv_latitude = (TextView) rl_layout.findViewById(R.id.tv_latitude);
viewHolder.tv_longitude = (TextView) rl_layout.findViewById(R.id.tv_longitude);
viewHolder.tv_battery = (TextView) rl_layout.findViewById(R.id.tv_battery);
viewHolder.tv_type = (TextView) rl_layout.findViewById(R.id.tv_type);
viewHolder.tv_ip = (TextView) rl_layout.findViewById(R.id.tv_ip);
rl_layout.setTag(viewHolder);
}
viewHolder = (ViewHolder) rl_layout.getTag();
BeanEvent be = (BeanEvent) marker.getObject();
viewHolder.tv_sim.setText("SIM::" + be.getSimId());
viewHolder.tv_count.setText("次数:" + be.getEventCount());
viewHolder.tv_sealNo.setText("公章号:" + be.getDeviceId());
viewHolder.tv_address.setText(result == null || result.getRegeocodeAddress() == null
|| result.getRegeocodeAddress().getFormatAddress() == null ? "地址:对不起,没有找到相关地理位置!" : "地址:" + result.getRegeocodeAddress().getFormatAddress() + "附近");
viewHolder.tv_type.setText("类型:" + be.getEventType());
viewHolder.tv_latitude.setText("东经:" + be.getLatitude());
viewHolder.tv_longitude.setText("北纬:" + be.getLongitude());
viewHolder.tv_battery.setText("电量:" + be.getBattery());
viewHolder.tv_ip.setText("IP:" + be.getEventIp());
final ViewHolder finalViewHolder = viewHolder;
viewHolder.tv_close.setOnClickListener(v ->
{
rl_layout.startAnimation(AnimUtils.goneAnim());
rl_layout.setVisibility(View.GONE);
});
}
class ViewHolder
{
TextView tv_address, tv_close, tv_sim, tv_count, tv_sealNo, tv_type, tv_latitude, tv_longitude, tv_battery, tv_ip;
}
最后,当我们点击,marker的时候,将布局显示出来,此处要实现marker的onclicklistener方法:
animMove(aMap, marker.getPosition()); // 带动画的移动地图到marker
MainActivity.instant.showToast("正在获取地址信息...");
// 发起地理编码请求
if (rl_info.getVisibility() == View.VISIBLE) // 如果当前已经有详情在显示,先Gone掉
{
rl_info.startAnimation(AnimUtils.goneAnim());
rl_info.setVisibility(View.GONE);
}
RegeocodeQuery query = new RegeocodeQuery(convertToLatLonPoint(marker.getPosition()), 200, GeocodeSearch.AMAP);
geoSearch.getFromLocationAsyn(query);// 设置异步逆地理编码请求
return false;
接下来,在实现地理位置的实现方法:
@Override
public void onRegeocodeSearched(RegeocodeResult result, int i)
{
if (i == AMapException.CODE_AMAP_SUCCESS)
{
setInfo(rl_info, marker, result); // 将结果显示在marker对应的详情布局上
rl_info.startAnimation(AnimUtils.showAnim());
rl_info.setVisibility(View.VISIBLE); // 显示
} else MainActivity.instant.showToast("获取地址失败!请稍后重试!");
}
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int i)
{
}
这样,我们就实现了图上的效果。
最后贴出MapUtils:
/**
* 把LatLng对象转化为LatLonPoint对象
*/
public static LatLonPoint convertToLatLonPoint(LatLng latlon)
{
return new LatLonPoint(latlon.latitude, latlon.longitude);
}
/**
* 把LatLonPoint对象转化为LatLon对象
*/
public static LatLng convertToLatLng(LatLonPoint latLonPoint)
{
return new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude());
}
/**********************************************************移动地图**************************************/
public static void animMove(AMap aMap, LatLng latLng)
{
aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));
}
public static void animMove(AMap aMap, LatLonPoint point)
{
aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(convertToLatLng(point), 15));
}
public static void animMove(AMap aMap, AMapLocation location)
{
aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location.getLatitude(), location.getLongitude()), 18));
}
至此,该功能已经完全实现。