如何解决Firebase按时间戳排序后地图标记自定义信息窗口显示旧数据的问题
问题根源
- 嵌套查询逻辑错误:你在
User Location的onChildAdded回调中监听了整个Users节点的子节点事件,这会导致每新增一个位置,就会把所有用户的数据都绑定到当前位置上生成标记,自然会先加载历史旧用户数据,再加载最新用户数据 - 监听器重复注册:每次触发
onChildAdded都会新增一个ChildEventListener,旧的监听器不会自动销毁,会多次重复触发回调生成冗余标记 - 无标记映射管理:每次拿到数据就新增标记,没有对应UID更新已有标记的逻辑,导致同一位置出现多个重叠标记,点击时优先展示旧标记的内容
修复方案
- 直接通过位置节点的UID查询对应用户的单条数据,不要监听整个
Users节点,User Location和Users两个节点的子节点key就是对应用户的UID,可以直接关联查询 - 提前初始化
HashMap存储UID与标记的映射关系,避免重复添加标记,数据更新时直接更新对应标记的内容 - 全局只设置一次
InfoWindowAdapter,不要在回调中重复设置
修复后核心代码
首先在Activity类中新增成员变量存储标记映射:
// 存储用户UID与对应地图标记的映射关系 private HashMap<String, Marker> uidMarkerMap = new HashMap<>();
修改后的onMapReady代码:
@Override public void onMapReady(@NonNull GoogleMap googleMap) { mMap = googleMap; // 全局只设置一次信息窗口适配器 mMap.setInfoWindowAdapter(new InfoWindowAdapter(Retrieve.this)); DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference(); DatabaseReference locationRef = rootRef.child("User Location"); locationRef.orderByChild("timestamp").addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) { // 拿到当前位置对应用户的UID String uid = snapshot.getKey(); LocationSend send = snapshot.getValue(LocationSend.class); LatLng location = new LatLng(send.getLatitude(), send.getLongitude()); // 直接查询对应用户的单条数据,无需监听整个Users节点 DatabaseReference userRef = rootRef.child("Users").child(uid); // 需要实时更新用户数据可以替换为addValueEventListener userRef.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { if (!snapshot.exists()) return; String name = snapshot.child("name").getValue(String.class); String emergencyType = snapshot.child("Emergency Type").getValue(String.class); String alertLevel = snapshot.child("Alert Level").getValue(String.class); // 先判断该用户是否已有标记,有则更新,无则新增 Marker marker = uidMarkerMap.get(uid); if (marker == null) { marker = mMap.addMarker(new MarkerOptions().position(location) .title(name) .snippet(emergencyType + "/" + alertLevel)); uidMarkerMap.put(uid, marker); } else { marker.setPosition(location); marker.setTitle(name); marker.setSnippet(emergencyType + "/" + alertLevel); // 如果当前标记的信息窗口正在展示,立即刷新内容 if (marker.isInfoWindowShown()) { marker.showInfoWindow(); } } // 按需调整摄像头移动逻辑,不用每次加载数据都移动 mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(location, 14F)); } @Override public void onCancelled(@NonNull DatabaseError error) { Log.e("TAG", "加载用户数据失败", error.toException()); } }); notification(); } @Override public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) { // 位置更新时同步更新对应标记的位置 String uid = snapshot.getKey(); LocationSend send = snapshot.getValue(LocationSend.class); LatLng location = new LatLng(send.getLatitude(), send.getLongitude()); Marker marker = uidMarkerMap.get(uid); if (marker != null) { marker.setPosition(location); if (marker.isInfoWindowShown()) { marker.showInfoWindow(); } } notification(); } @Override public void onChildRemoved(@NonNull DataSnapshot snapshot) { // 用户位置删除时同步移除对应标记 String uid = snapshot.getKey(); Marker marker = uidMarkerMap.remove(uid); if (marker != null) { marker.remove(); } } @Override public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {} @Override public void onCancelled(@NonNull DatabaseError error) {} }); }
内容的提问来源于stack exchange,提问作者Rafael Campos
相关产品推荐
相关产品推荐

