Android Loading标记动态最佳策略

时间:2017-03-14 17:03:37

标签: android google-maps dynamic

我有一个谷歌地图的Android地图,根据用户屏幕位置从服务器动态加载标记。因此,例如,如果用户移动地图,我只是向服务器发出请求,并且我正在发送屏幕边界,并且基于此我得到标记数据(id和corrdinates),稍后将其解析并创建为实际指针。问题是,当用户返回到同一区域(之前创建了哪些标记)时,我仍然提出相同的请求并获得相同的数据(但显然我不会让重新创建该标记,所以我只是在那里运行所有标记的循环在地图上并检查地图标记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(我可以选择屏幕范围内的所有标记以及哪些ID不在屏幕范围内)
  • 每次都从android应用程序中删除标记,并且每次都基本上从服务器重新创建所有标记(我个人认为这是一个糟糕的解决方案)

那么哪些想法最好?还有其他想法吗? (对不起我的英文)

3 个答案:

答案 0 :(得分:2)

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

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

我只会为您的四叉树提供伪ish 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;
}

答案 1 :(得分:1)

将所有可见标记存储在列表或本地Db或某些位置的屏幕上,如果不存在来自服务器的请求,则迭代现有标记列表

答案 2 :(得分:1)

扩展HashSet<MarkerOptions>,以便在添加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调用服务器请求。如果您在currentBoundscurrentBounds重叠时设法缩短mBounds,则可以改进此算法。

<强>假设

请注意,这里的假设是当相机移动时,currentBounds会略微偏离mBounds。否则,不会加载新标记。 参考图像,如果用户从可见绿色区域移动到橙色,则mBounds值将是蓝色矩形,因此不会覆盖白色区域。但是,实际上,水平或垂直移动相机并不容易,这将导致调用服务器方法和新标记,以便加载未受影响的区域。

How mBounds will be aggregated?

希望这会有所帮助。 :)