【问题标题】:Android Loading markers dynamically best strategyAndroid 动态加载标记的最佳策略
【发布时间】:2017-08-05 03:44:34
【问题描述】:

我有一个带有 google 地图的 android 地图,它可以根据用户屏幕位置从服务器动态加载标记。因此,例如,如果用户移动地图,我只需向服务器发出请求并发送屏幕边界,并基于此获得标记数据(id 和 corrdinates),这些数据稍后会被解析并创建为实际指针。问题是当用户回到同一个区域(之前创建了哪些标记)时,我仍然发出相同的请求并获得相同的数据(但显然我不会让重新创建该标记,所以我只是运行 for 循环遍历那里的所有标记在地图上并检查地图标记 ID 是否等于服务器发送数据标记 ID,如果相等,我只需打破循环)

try {
                Collection<MarkerItemData> mapMarkers = algorithm.getItems();
                JSONObject jsonObject = new JSONObject(strings[0]);
                JSONArray respondArray = jsonObject.getJSONArray("respond");
                list = new ArrayList();
                for (int i = 0; i < respondArray.length(); i++) {
                    JSONObject station = respondArray.getJSONObject(i);
                    int id = station.getInt("_id");
                    boolean skip = false;
                    for (final MarkerItemData m : mapMarkers) {
                        if (m.getId() == id) {
                            skip = true;
                            break;
                        }
                    }
                }
            }

但是我不认为这种方法是最好的。我还有其他想法应该可行(至少我认为)

  • 发送到服务器屏幕边界以及屏幕上可见的所有标记 ID(我可以选择屏幕范围内的所有标记以及屏幕边界之外的所有标记)
  • 每次从 android 应用程序中删除标记并基本上每次从服务器重新创建所有标记(我个人认为这是一个糟糕的解决方案)

那么这些想法中哪一个是最好的?还有其他想法吗? (对不起我的英语)

【问题讨论】:

  • 每次移动地图时都需要获取标记的位置吗?你不能先下载所有并显示标记吗?
  • @WaqasAhmedAnsari 当服务器上的总标记数很大时,预先获取所有标记是低效的

标签: android google-maps dynamic


【解决方案1】:

向服务器发送屏幕边界和屏幕上当前可见标记的 id-s 是您最好的选择。但是仍然有几个麻烦的问题。您将如何找到包含在您指定的屏幕边界范围内的所有标记?如果新的标记出现在您的服务器上或某些标记被删除了怎么办?在这种情况下,您是否可以提供可维护的结构,或者您会逐个测试与数据库中的标记相对应的每个点,看它是否位于范围内?考虑到所有这些,您需要找到一种方法来优化存储和查询点,即点标记的纬度和经度对。您应该使用一种常见的空间索引方法来执行空间索引。

空间索引的方法有很多,根据用例,一种方法可能比另一种略好。长话短说,由于您需要在这种情况下查询范围,因此您应该实现四叉树。四叉树被称为树数据结构,其中每个内部节点正好有四个孩子(西北、东北、西南、东南)。如果你对这个结构一无所知,我相信你可以在一个小时内了解它的基础知识,但是从头开始详细解释它会浪费我太多时间。因此,我将跳过有关四叉树的实现细节。有几个来源已经比我更好地解释了这个结构,你可以很容易地找到一个开源库。

我只会为您的四叉树提供伪 Java 方法来查找出现在屏幕边界范围内的所有点,不包括已经在前一个屏幕中的点:

ArrayList<LatLng> queryRange(QuadLevel level, float[] screenBounds, ArrayList<LatLng> prevPoints) { 

    // Initialize a list to hold the found points
    ArrayList<LatLng> pointsInRange = new ArrayList<>();

    if (!quadtreeBounds.intersects(screenBounds))
        return pointsInRange;

    // Find the points that are in the current quad level
    for (LatLng point : level.getPoints()) {
        // If the current point is in screen bounds and it is not contained by prevPoints
        if (point.isInRange(screenBounds) 
            && !prevPoints.contains(point))
            pointsInRange.add(point);
    }

    // If there are no children, return
    if (level.hasNoChildren())
        return pointsInRange;

    // Else, continue to look up children
    pointsInRange.addAll(queryRange(level.northwest, screenBounds, prevPoints));
    pointsInRange.addAll(queryRange(level.northeast, screenBounds, prevPoints));
    pointsInRange.addAll(queryRange(level.southwest, screenBounds, prevPoints));
    pointsInRange.addAll(queryRange(level.southeast, screenBounds, prevPoints));

    return pointsInRange;
}

【讨论】:

    【解决方案2】:

    将屏幕上的所有可见标记存储在列表或本地数据库或某个地方,如果不存在来自服务器的请求,则迭代列表中的现有标记

    【讨论】:

      【解决方案3】:

      扩展HashSet&lt;MarkerOptions&gt;,以便在添加MarkerOptions 时将其添加到地图中。

      class MarkerSet extends HashSet<MarkerOptions> {
          @Override
          public boolean add(MarkerOptions markerOptions) {
      
              boolean didAdd = super.add(markerOptions);
      
              if(didAdd) {
                  mMap.addMarker(markerOptions);
              }
              return didAdd;
          }
      }
      

      在您的地图活动中维护一个 LatLngBounds 对象。这个 LatLngBounds 对象将存储您已经从服务器请求数据的边界。

      当地图的相机移动时,检查新边界是否在当前存储的范围内,如果不请求服务器并增加边界,则不请求服务器。

      mMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
          @Override
          public void onCameraMove() {
      
              LatLngBounds currentBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
      
              if (mBounds == null) {
                  mBounds = currentBounds;
                  // Perform server request and get markers passing in currentBounds
                  // Following to be performed in server request's callback
                  MarkerOptions markerOptions = new MarkerOptions();
                  // set marker options here
                  mMarkerSet.add(markerOptions);
      
              } else {
      
                  if (!(mBounds.contains(currentBounds.northeast) || mBounds.contains(currentBounds.southwest))) {
                      mBounds = mBounds.including(currentBounds.northeast);
                      mBounds = mBounds.including(currentBounds.southwest);
      
                      // Perform server request and get markers passing in currentBounds
                      // Following to be performed in server request's callback
                      MarkerOptions markerOptions = new MarkerOptions();
                      // set marker options here
                      mMarkerSet.add(markerOptions);
                  }
      
              }
          }
      });
      

      确保您始终为currentBounds 调用服务器请求。如果您在currentBoundsmBounds 重叠时设法缩短currentBounds,则可以改进此算法。

      假设

      请注意,这里的假设是当相机移动时,currentBounds 会稍微偏离mBounds。否则,不会加载新标记。 参考图像,如果用户从可见的绿色区域移动到橙色,mBounds 值将是蓝色矩形,因此白色区域不会被覆盖。然而,在实践中,将相机完美地水平或垂直移动并不容易,这将导致调用服务器方法并为未冒险区域加载新的标记。

      希望这会有所帮助。 :)

      【讨论】:

        猜你喜欢
        • 2011-06-30
        • 2022-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多