{"id":13645378,"url":"https://github.com/yipianfengye/android-togetherMap","last_synced_at":"2025-04-21T14:30:41.358Z","repository":{"id":112651691,"uuid":"71094317","full_name":"yipianfengye/android-togetherMap","owner":"yipianfengye","description":"实现高德地图的marker聚合功能","archived":false,"fork":false,"pushed_at":"2016-10-21T01:12:54.000Z","size":2969,"stargazers_count":89,"open_issues_count":3,"forks_count":28,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-08-02T01:25:20.519Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yipianfengye.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-17T02:50:21.000Z","updated_at":"2024-07-20T03:55:18.000Z","dependencies_parsed_at":"2023-06-02T08:15:30.510Z","dependency_job_id":null,"html_url":"https://github.com/yipianfengye/android-togetherMap","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yipianfengye%2Fandroid-togetherMap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yipianfengye%2Fandroid-togetherMap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yipianfengye%2Fandroid-togetherMap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yipianfengye%2Fandroid-togetherMap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yipianfengye","download_url":"https://codeload.github.com/yipianfengye/android-togetherMap/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223868079,"owners_count":17217025,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T01:02:34.215Z","updated_at":"2024-11-09T18:31:05.372Z","avatar_url":"https://github.com/yipianfengye.png","language":"Java","readme":"# android-togetherMap\n\n本文我将讲解一下我最近实现的高德地图Marker的聚合功能。在项目开发中需要使用到地图Marker的聚合功能，但是高德地图并没有实现对Marker的聚合功能，所以需要自己实现其聚合功能（这里说一下百度是实现的，为啥高德不做？）下面我将介绍一下具体的实现步骤。\n\n本项目的github地址：\u003ca href=\"https://github.com/yipianfengye/android-togetherMap\"\u003eandroid-togetherMap\u003c/a\u003e\n\n本项目的具体实现效果如下：\n\n![image](http://img.blog.csdn.net/20161019092142793)\n\n**（一）集成高德地图API**\n\n- 参考高德地图开放平台文档\n\n高德地图API集成参考地址：\u003ca href=\"http://lbs.amap.com/\"\u003e高德开放平台\u003c/a\u003e\n\n包括配置Manifest，申请AppId等步骤；\n\n\n- 添加Layout布局文件\n\n```\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cRelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\u003e\n\n    \u003ccom.amap.api.maps.MapView\n        android:id=\"@+id/map\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" /\u003e\n\n\u003c/RelativeLayout\u003e\n```\n这里主要是添加MapView控件，该控件是地图显示控件，也是我们实现地图功能的主要控件。\n\n- 执行Map对象初始化以及各个生命周期方法\n\n```\n@Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        mapView = (MapView) findViewById(R.id.map);\n        mapView.onCreate(savedInstanceState);// 此方法必须重写\n        init();\n    }\n\n    /**\n     * 初始化AMap对象\n     */\n    private void init() {\n        if (aMap == null) {\n            aMap = mapView.getMap();\n        }\n\n        aMap.setOnCameraChangeListener(onCameraChangeListener);\n\n        dotList.clear();\n        dotList = DotInfo.initData();\n    }\n\n/**\n     * 方法必须重写\n     */\n    @Override\n    protected void onResume() {\n        super.onResume();\n        mapView.onResume();\n\n        updateNormalMarkers();\n    }\n\n    /**\n     * 方法必须重写\n     */\n    @Override\n    protected void onPause() {\n        super.onPause();\n        mapView.onPause();\n    }\n\n    /**\n     * 方法必须重写\n     */\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n        mapView.onSaveInstanceState(outState);\n    }\n\n    /**\n     * 方法必须重写\n     */\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        mapView.onDestroy();\n    }\n```\n主要是在Activity的生命周期方法中添加对MapView的处理...\n\n\n- 地图页面中添加Marker对象\n\n```\n/**\n     * 初始化marker数据\n     */\n    private void loadMarker(List\u003cDotInfo\u003e dotList) {\n        if (dotList == null || dotList.size() == 0) {\n            return;\n        }\n\n        for (int i = 0; i \u003c dotList.size(); i++) {\n            DotInfo dotInfo = dotList.get(i);\n\n            MarkerOptions options = new MarkerOptions();\n            options.anchor(0.5f, 1.0f);\n            options.position(new LatLng(dotInfo.getDotLat(), dotInfo.getDotLon()));\n\n            setIconToOptions(options);\n\n            Marker marker = aMap.addMarker(options);\n            marker.setObject(dotInfo);\n            marker.setZIndex(ORGZOON);\n\n            markerMap.put(dotInfo.getDotId(), marker);\n        }\n    }\n```\n\n删除方法中传递的参数是我在客户端写死的集合数据，执行到这里至此我们就实现了一个简单的地图页面，并添加了相应的Marker对象，具体效果如下：\n\n\u003cimg src=\"http://img.blog.csdn.net/20161019092631689\" width=\"300\" height=\"500\"\u003e\n\n\n**（二）添加地图缩放聚合功能**\n\n在上面我们已经实现了一个简单的地图显示页面以及添加了几个Marker对象，现在需要实现的效果是当我们扩大地图的显示比例的时候，相邻的Marker会聚合成一个合成的Marker对象，具体的实现效果就是我们一开始展示的效果。\n\n- 重写地图的OnCameraChangeListener\n\n该Listener会在地图移动或者是改变显示比例的时候被回调，这里我们再其回调方法onCameraChangeFinish方法中执行我们的Marker计算，聚合功能，具体代码如下：\n\n```\n/**\n     * 设置地图移动监听\n     */\n    private AMap.OnCameraChangeListener onCameraChangeListener = new AMap.OnCameraChangeListener() {\n        @Override\n        public void onCameraChange(CameraPosition cameraPosition) {\n        }\n\n        @Override\n        public void onCameraChangeFinish(CameraPosition cameraPosition) {\n            // 放大缩小完成后对聚合点进行重新计算\n            updateMapMarkers();\n        }\n    };\n```\n\n好吧，然后我们看一下自定义方法的updateMapMakers方法的实现逻辑：\n\n```\n/**\n * 主要用于更改计算聚合Marker对象\n */\nprivate synchronized void updateMapMarkers() {\n        if (dotList != null \u0026\u0026 dotList.size() \u003e 0) {\n            Log.i(TAG, \"地图级别:\" + aMap.getCameraPosition().zoom);\n            // 若当前地图级别小于初始化比例尺,则显示聚合网点\n            if (aMap.getCameraPosition().zoom \u003c ORGZOON) {\n                 markerStatus = MARKER_TOGE;\n                 updateTogMarkers();\n            }\n            // 显示普通marker\n            else {\n                if (markerStatus == MARKER_TOGE) {\n                    markerStatus = MARKER_NORMA;\n                    updateNormalMarkers();\n                }\n            }\n\n            System.gc();\n        }\n    }\n```\n\n地图缩放操作有两部分，分别为展示普通marker部分和展示聚合marker部分，这里地图显示比例有一个阙值，当我们的显示比例小于这个阙值的时候计算结果显示聚合Marker对象，当显示比例大于这个阙值的时候计算结果显示普通的Marker对象。\n\n\n**（三）展示聚合Marker操作**\n\n下面开始我们就将执行具体的Marker对象的聚合功能了\n\n```\n/**\n     * 更新聚合网点\n     */\n    private void updateTogMarkers() {\n\n        Log.i(TAG, \"开始显示聚合网点,清空地图normal marker...\");\n        aMap.clear();\n        // 更新聚合marker\n        MapTogetherManager.getInstance(this, aMap).onMapLoadedUpdateMarker(markerMap);\n\n        // 设置marker点击事件,若是聚合网点此时点击marker则放大地图显示正常网点\n        aMap.setOnMarkerClickListener(new AMap.OnMarkerClickListener() {\n            @Override\n            public boolean onMarkerClick(Marker marker) {\n                // 初始化地图按指定的比例尺移动到定位的坐标\n                aMap.animateCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition(marker.getPosition(), ORGZOON, 3, 0)), 1000, null);\n                return true;\n            }\n        });\n    }\n```\n\n我们可以发现具体的Marker聚合操作是在MapTogetherManager类中实现的，所以我们需要看一下MaoTigetherManager的实现逻辑。\n\n```\n/**\n     * 更新聚合网点\n     */\n    public void onMapLoadedUpdateMarker(final Map\u003cString, Marker\u003e markerMap) {\n        // 清空内存聚合网点数据\n        togDotInfoMap.clear();\n        togMarkerMap.clear();\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                synchronized (lockObject) {\n                    Log.i(TAG, \"开始循环遍历,执行网点聚合操作...\");\n                    Iterator\u003cMap.Entry\u003cString, Marker\u003e\u003e iterator = markerMap.entrySet().iterator();\n                    // 循环遍历在已有的聚合基础上，对添加的单个元素进行聚合\n                    while (iterator.hasNext()) {\n                        assignSingleCluster(iterator.next().getValue());\n                    }\n                    Log.i(TAG, \"开始执行将聚合网点展示在地图上...\");\n                    // 将聚合的网点现在在地图上\n                    if (togDotInfoMap != null \u0026\u0026 togDotInfoMap.size() \u003e 0) {\n                        Iterator\u003cMap.Entry\u003cString, TogDotInfo\u003e\u003e cIterator = togDotInfoMap.entrySet().iterator();\n                        while (cIterator.hasNext()) {\n                            Map.Entry\u003cString, TogDotInfo\u003e togMap = cIterator.next();\n                            addTogDotInfoToMap(togMap.getKey(), togMap.getValue());\n                        }\n                    }\n                }\n            }\n        }).start();\n    }\n```\n可以发现在其中我们调用了assignSingleCluster方法对Marker对象进行计算判断其属于哪个聚合Marker对象，具体assignSingleCluster方法的实现逻辑：\n\n```\n/**\n     * 在已有的聚合基础上，对添加的单个元素进行聚合\n     */\n    private void assignSingleCluster(Marker marker) {\n        DotInfo dotInfo = (DotInfo) marker.getObject();\n        LatLng latLng = new LatLng(dotInfo.getDotLat(), dotInfo.getDotLon());\n\n        Point point = aMap.getProjection().toScreenLocation(latLng);\n        TogDotInfo togDotInfo = getCluster(point);\n        if (togDotInfo != null) {\n            togDotInfo.addClusterItem(marker);\n            // 更新聚合网点个数\n            togDotInfo.setDotCount(togDotInfo.getDotCount() + 1);\n        } else {\n            togDotInfo = new TogDotInfo(point, latLng);\n            // 更新聚合网点个数\n            togDotInfo.setDotCount(1);\n            togDotInfo.addClusterItem(marker);\n            togDotInfoMap.put(dotInfo.getDotId() + SystemClock.currentThreadTimeMillis(), togDotInfo);\n        }\n    }\n```\n\n好吧，具体的实现逻辑下放到了getCluster方法中：\n\n```\n/**\n     * 根据一个点获取是否可以依附的聚合点，没有则返回null\n     * @param point\n     * @return\n     */\n    private TogDotInfo getCluster(Point point) {\n        Iterator\u003cMap.Entry\u003cString, TogDotInfo\u003e\u003e cIterator = togDotInfoMap.entrySet().iterator();\n        while (cIterator.hasNext()) {\n            TogDotInfo togDotInfo = cIterator.next().getValue();\n            Point poi = togDotInfo.getCenterPoint();\n            double distance = getDistanceBetweenTwoPoints(point.x, point.y, poi.x, poi.y);\n            if (distance \u003c CLUSTER_SIZE) {\n                return togDotInfo;\n            }\n        }\n\n        return null;\n    }\n```\n\n这样经过一系列的操作之后我们就生成了聚合Marker对象列表并将其放置到我们的togDotInfoMap对象中，然后我们调用了addTogDotInfoToMap将聚合Marker对象添加到地图页面中。\n\n```\n/**\n     * 将聚合网点添加至地图显示\n     * @param togDotInfo 需要更新的聚合网点对象\n     */\n    private void addTogDotInfoToMap(String dotId, TogDotInfo togDotInfo) {\n        LatLng latlng = togDotInfo.getCenterLatLng();\n        MarkerOptions markerOptions = new MarkerOptions();\n        markerOptions.anchor(0.5f, 0.5f).icon(getBitmapDes(togDotInfo)).position(latlng);\n        Marker marker = aMap.addMarker(markerOptions);\n        togMarkerMap.put(dotId, marker);\n    }\n```\n\n这样我们最终将聚合的Marker对象绘制到了地图中，这其中绘制聚合Marker对象的样式都是可定制的，这里只是简单的绘制成一个黑色的圆形，也可以扩充一下，比如当数据大于10的时候一个颜色，当数据大于20的时候一个颜色等等，具体的可参考代码。\n\n\n\n**总结：**\n\n本文主要是通过自定义的地图Marker的聚合算法，实现了Marker对象的聚合功能，这里定制化内容较多就不做成库了，具体可参考我的：\u003ca href=\"https://github.com/yipianfengye/android-togetherMap\"\u003eandroid-togetherMap\u003c/a\u003e。另外也可参考我的博客：\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/52063010\"\u003e快速实现自定义地图聚合操作\u003c/a\u003e\n\n\n\n\n\u003cbr\u003e另外对github项目，开源项目解析感兴趣的同学可以参考我的：\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/50592352\"\u003e github项目解析（四）--\u003e动态更改TextView的字体大小\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/51707796\"\u003e github项目解析（五）--\u003eAndroid日志框架\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/51713824\"\u003e github项目解析（六）--\u003e自定义实现ButterKnife框架\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/51730472\"\u003egithub项目解析（七）--\u003e防止按钮重复点击\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/51764304\"\u003eGithub项目解析（八）--\u003eActivity启动过程中获取组件宽高的五种方式\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/51821159\"\u003eGithub项目解析（九）--\u003e实现Activity跳转动画的五种方式\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/52037710\"\u003eGithub项目解析（十）--\u003e几行代码快速集成二维码扫描库\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/52121633\"\u003eGithub项目解析（十一）--\u003e一个简单，强大的自定义广告活动弹窗\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/52593674\"\u003eGithub项目解析（十二）--\u003e一个简单的多行文本显示控件\u003c/a\u003e\n\u003cbr\u003e\u003ca href=\"http://blog.csdn.net/qq_23547831/article/details/52593670\"\u003eGithub项目解析（十三）--\u003e使用Kotlin实现UC头条ViewPager左右滑动效果\u003c/a\u003e\n","funding_links":[],"categories":["地图"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyipianfengye%2Fandroid-togetherMap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyipianfengye%2Fandroid-togetherMap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyipianfengye%2Fandroid-togetherMap/lists"}