您好,欢迎来到源码搜藏网!分享精神,快乐你我!
[加入VIP] 设为首页 | 收藏本站 | 网站地图 | Sitemap | TAG标签
  • 首 页
  • 在线工具
  • jquery手册
  • 当前位置:首页 > 安卓源码 > 技术博客 >

    Android开发高德地图和google地图适配

    时间:2018-07-18 09:27 来源:互联网 作者:源码搜藏 浏览:收藏 挑错 推荐 打印

    今天介绍的是大型app必备模块 - 地图模块。 当今世界最大的地图sdk应该是google地图,但是由于国内墙掉了google play service,国内是无法使用google地图的,然而国内比较热门的地图sdk是高德地图和百度地图。(如果你是IOS,还有自带的地图) 近来项目中需

    今天介绍的是大型app必备模块 - 地图模块。
    当今世界最大的地图sdk应该是google地图,但是由于国内墙掉了google play service,国内是无法使用google地图的,然而国内比较热门的地图sdk是高德地图和百度地图。(如果你是IOS,还有自带的地图)

    近来项目中需要世界地图,所以特此做了一个高德地图和谷歌地图兼容的模块了。

    SDK接入

    1.google地图,接入相对比较简单,当然因为Android本身就是google亲儿子的原因。
    需要引入google service's sdk,以及google map的sdk 
    https://developers.google.com/places/android-api/ start,获取账号需要gmail邮箱作为管理

    2.高德地图接入相对比较复杂一点,可以选择2D,3D,定位,搜索多种模块去接入地图。
    然后需要申请账号,随便邮箱手机号就可以了,通过keytools命令提出的密钥库的SHA1值,包名和
    SHA1值相互绑定的,每次请求都会验证。
    然后配置AndroidManifest中的元数据。

    预览模块

    Android开发高德地图和google地图适配

    1.高德地图是通过sdk提供的com.amap.api.maps2d.MapView自定义地图查看来实现的。google 
    地图是通过sdk提供一个Fragment空间来实现地图获取

    <fragment xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/google_map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
    mapFragment = childFragmentManager.findFragmentById(R.id.google_map) as SupportMapFragment

    2.地图预览

    Android开发高德地图和google地图适配

    谷歌地图和高德地图接口相关的名字都是差不多的,比较常用的接口
    moveCamera视窗转移缩放级别分为1~17级,数值越大地图越
    精准addMarker添加地图标签

    谷歌地图是使用getMapAysnc,会有onMapReady的接口回调

            mapFragment?.getMapAsync(this)
    
        /**
         * 地图就绪
         */
        override fun onMapReady(googleMap: GoogleMap?) {
            googleMap ?: return
            with(googleMap) {
                val latLng = LatLng(latitude, longitude)
                //视觉转移
                moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16f))
                //添加坐标
                addMarker(MarkerOptions().position(latLng))
            }
        }

    高德地图使用setOnMapLoadedListener方法来设置回调

    aMap?.setOnMapLoadedListener {
                //视觉移动
                aMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(latitude, longitude), 100f))
                //添加坐标
                aMap?.addMarker(MarkerOptions().anchor(0.5f, 0.5f)
                        .icon(BitmapDescriptorFactory
                                .fromBitmap(BitmapFactory.decodeResource(
                                        resources, R.drawable.common_drag_location)))
                        .position(LatLng(latitude, longitude)))
            }

    如果不想地图的坐标和视觉点显示居中怎么办
    需要将布局中缘上着手,如果想要往上移,就需要将marginTop设置为负值,这样地图中心点就会上移动,并且视觉点也会和中心点一样上移。

    定位模块

    Android开发高德地图和google地图适配

    1.高德提供了AMapLocationListener作为专为提供高德坐标的监听

          private fun setUpMap() {
            myLocationStyle = MyLocationStyle()
            myLocationStyle?.strokeColor(Color.argb(0, 0, 0, 0))// 设置圆形的边框颜色
            myLocationStyle?.radiusFillColor(Color.argb(0, 0, 0, 0))// 设置圆形的填充颜色
            myLocationStyle?.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.common_self_location))  //显示自身定位坐标
            aMap?.setMyLocationStyle(myLocationStyle)
            aMap?.isMyLocationEnabled = true// 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
            val uriSettings = aMap?.uiSettings
            uriSettings?.isZoomControlsEnabled = false //关掉缩放键
        }
    
    
        private fun initLoc() {
            //初始化定位
            mLocationClient = AMapLocationClient(context!!.applicationContext)
            //设置定位回调监听
            mLocationClient?.setLocationListener(this)
            //初始化定位参数
            mLocationOption = AMapLocationClientOption()
            //设置定位模式为高精度模式,Battery_Saving为低功耗模式,Device_Sensors是仅设备模式
            mLocationOption?.locationMode = AMapLocationClientOption.AMapLocationMode.Hight_Accuracy
            //设置是否返回地址信息(默认返回地址信息)
            mLocationOption?.isNeedAddress = true
            //设置是否只定位一次,默认为false
            mLocationOption?.isOnceLocation = false
            //设置是否强制刷新WIFI,默认为强制刷新
            mLocationOption?.isWifiActiveScan = false
            //设置是否允许模拟位置,默认为false,不允许模拟位置
            mLocationOption?.isMockEnable = false
            //设置定位间隔,单位毫秒,默认为3000ms
            mLocationOption?.interval = (3000)
            //给定位客户端对象设置定位参数
            mLocationClient?.setLocationOption(mLocationOption)
            //启动定位
            mLocationClient?.startLocation()
        }
    
     override fun onLocationChanged(amapLocation: AMapLocation?) {
           //监听实时定位
        }

    谷歌的定位是使用的Android原生的位置定位

    locationManager = context!!.getSystemService(Context.LOCATION_SERVICE) as LocationManager
    locationManager?.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 10f, this)
                locationManager?.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 3000, 10f, this)
       
     /**
      *地图就绪
       */
        override fun onMapReady(googleMap: GoogleMap?) {
            googleMap ?: return
            this.googleMap = googleMap
            with(googleMap.uiSettings) {
                isZoomGesturesEnabled = true
                isMyLocationButtonEnabled = true
                isScrollGesturesEnabled = true
            }
            try {
                googleMap.isMyLocationEnabled = true
            } catch (e: SecurityException) {
                ALog.e(TAG, e)
            }
       }
    
       /**
         * 定位更新
         */
        override fun onLocationChanged(location: Location?) {
          
        }
    

    搜索模块

    Android开发高德地图和google地图适配

    1.Google搜索有两种方式,一种是通过webapi来搜索出附近相关的地点(这里使用了RxVolley的框架),这个的好处关联结果比较多。
    这里不用Uri.Builder的拼接方式是因为其指定了UTF-8的格式转换将会出现“”强转为“%”号

        val googlePlaceUrl = "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
        fun getGoogleNearByPlaces(latitude: Double, longitude: Double, radius: Int): Observable<GoogleLocation> {
            val builder = StringBuilder(googlePlaceUrl)
            builder.append("?location=").append(latitude.toString()).append(",").append(longitude.toString())
            builder.append("&amp;radius=").append(radius.toString())
            builder.append("&amp;key=").append(googlePlaceKey)
            return RxVolley.get<GoogleLocation>(builder.toString(), null, object : TypeToken<GoogleLocation>() {}.type)
        }

    第二种是文字关联搜索(
    这边是使用了需要高级定义搜索,所以使用了适配器的形式。

        override fun doSearch(key: String, city: String?) {
            Observable.create(ObservableOnSubscribe<ArrayList<AutocompletePrediction>> {
                it.onNext(getAutocomplete(key)!!) //需要在次线程
            }).subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe({
                        searchAdpater?.clearData()
                        for (item in it) {
                            val placeResult = mGeoDataClient!!.getPlaceById(item.placeId) 
                            placeResult.addOnCompleteListener(mUpdatePlaceDetailsCallback)  //异步访问单个placeId的详细信息
                        }
                    }, {
                        ALog.e(TAG, it)
                    })
        }
    
        /**
         * 异步访问单个placeId的详细信息
         */
        val mUpdatePlaceDetailsCallback = object : OnCompleteListener<PlaceBufferResponse> {
            override fun onComplete(task: Task<PlaceBufferResponse>) {
                try {
                    val place = task.result.get(0)
                    searchAdpater?.addData(LocationItem(false, place.latLng.latitude, place.latLng.longitude, place.name.toString(), place.address.toString()))
                    ALog.i(TAG, "Place details received: " + place.name)
                    task.result.release()
                } catch (e: RuntimeRemoteException) {
                    ALog.e(TAG, e)
                }
            }
        }
    
    private fun getAutocomplete(constraint: CharSequence): ArrayList<AutocompletePrediction>? {
            ALog.d(TAG, "Starting autocomplete query for: " + constraint)
    
            // Submit the query to the autocomplete API and retrieve a PendingResult that will
            // contain the results when the query completes.
            val results = mGeoDataClient?.getAutocompletePredictions(constraint.toString(), null,
                    null)
    
            // This method should have been called off the main UI thread. Block and wait for at most
            // 60s for a result from the API.
            //收集文字关联预测结果
            try {
                Tasks.await<AutocompletePredictionBufferResponse>(results!!, 2, TimeUnit.SECONDS)
            } catch (e: ExecutionException) {
                e.printStackTrace()
            } catch (e: InterruptedException) {
                e.printStackTrace()
            } catch (e: TimeoutException) {
                e.printStackTrace()
            }
    
            try {
                val autocompletePredictions = results!!.result
    
                ALog.d(TAG, "Query completed. Received " + autocompletePredictions.count
                        + " predictions.")
    
                // Freeze the results immutable representation that can be stored safely.
                return DataBufferUtils.freezeAndClose<AutocompletePrediction, AutocompletePrediction>(autocompletePredictions)
            } catch (e: RuntimeExecutionException) {
                // If the query did not complete successfully return null
                Toast.makeText(context, "Error contacting API: " + e.toString(),
                        Toast.LENGTH_SHORT).show()
                ALog.e(TAG, "Error getting autocomplete prediction API call", e)
                return null
            }
    
        }

    2.高德地图中的PoiSearch是支持通过关键字搜索和经纬度地址附近搜索。

        /**
         * 经纬度搜索
         */
        fun doSearchQuery(city: String, latitude: Double, longtitude: Double) {
            query = PoiSearch.Query("", "", city) // 第一个参数表示搜索字符串,第二个参数表示poi搜索类型,第三个参数表示poi搜索区域(空字符串代表全国)
            query?.pageSize = 20 // 设置每页最多返回多少条poiitem
            query?.pageNum = 1  // 设置查第一页
            val poiSearch = PoiSearch(context, query)
            poiSearch.setOnPoiSearchListener(onPoiSearchListener)
            // 设置搜索区域为以lp点为圆心,其周围5000米范围
            poiSearch.bound = PoiSearch.SearchBound(LatLonPoint(latitude, longtitude), 1000, true)
            poiSearch.searchPOIAsyn()  // 异步搜索
        }
    
        /**
         * 关键字搜索
         */
        fun doSearchQuery(keyWord: String, city: String?) {
            if (city != null)
                query = PoiSearch.Query(keyWord, "", city)
            else
                query = PoiSearch.Query(keyWord, "", "")
            query?.pageSize = 20 // 设置每页最多返回多少条poiitem
            query?.pageNum = 1  // 设置查第一页
    
            val poiSearch = PoiSearch(context!!, query)
            poiSearch.setOnPoiSearchListener(onSearchListener)
            poiSearch.searchPOIAsyn()  // 异步搜索
        }

    当然也支持异步返回

        /**
         * 搜索结果
         */
        val onSearchListener = object : PoiSearch.OnPoiSearchListener {
            override fun onPoiSearched(result: PoiResult?, rCode: Int) {
                if (rCode == 1000) {
                    if (result?.query!! == query) {
                        //返回结果列表
                        result.pois 
                    }
                }
            }
    
            override fun onPoiItemSearched(p0: PoiItem?, p1: Int) {
                   //返回再搜索
            }
        }

    地图缩略图获取

    Android开发高德地图和google地图适配

    1.高德地图和google地图都需要使用web api来获取缩略图

            var builder: StringBuilder? = null
            if (type == GDMAP) {
                builder = StringBuilder(gdImgUrl)
                builder.append("?location=").append(longitude).append(",").append(latitude)
                builder.append("&amp;zoom=").append(zoom)
                builder.append("&amp;size=").append(dpToPx(context, width)).append("*").append(dpToPx(context, height))
                builder.append("&amp;markers=").append("mid").append(",").append(",A:").append(longitude).append(",").append(latitude)
                builder.append("&amp;key=").append(gdMapKey)
            } else if (type == GOOGLEMAP) {
                builder = StringBuilder(googleImgeUrl)
                builder.append("?center=").append(latitude).append(",").append(longitude)
                builder.append("&amp;zoom=").append(zoom)
                builder.append("&amp;size=").append(dpToPx(context, width)).append("x").append(dpToPx(context, height))
                builder.append("&amp;markers=").append(latitude).append(",").append(longitude)
                builder.append("&amp;key=").append(googlePlaceKey)
            }
    
            return builder.toString()

    这里需要注意的是

    1.高德大小的拼接,是用*号,而谷歌大小的拼接是使用“x” 
    。2.google需要使用的是placeKey,不是
    mapkey.3 两种地图的缩略图都不支持自定义标记(标记)
    4。高德地图无法显示到国外google的地址的详细信息
    [高德缩略图说明](
    <a href =“https://developers.google.com/maps/documentation/maps-static/intro ?hl = zh-cn)[google缩略图说明]

    特殊转换
    1.高德地图使用的是高德坐标,并不是标准的的GPS坐标。而高德只提供了其他地图和GPS转高德坐标,并不没有提供高德转为其他坐标。谷歌使用的标准的GPS坐标,那么如果需要坐标互通就需要相互转换了,这里提供了坐标转换相互转换的方式

    object GDConverter {
        fun fromGpsToLatLng(context: Context, latitude: Double, longitude: Double): LatLng? {
    
            val converter = CoordinateConverter(context)
            converter.from(CoordinateConverter.CoordType.GPS)
            try {
                converter.coord(DPoint(latitude, longitude))
                val desLatLng = converter.convert()
                return LatLng(desLatLng.latitude, desLatLng.longitude)
            } catch (e: Exception) {
                e.printStackTrace()
            }
    
            return null
        }
    
        /**
         * GPS坐标转换成高德
         */
        fun toGDLatLng(latitude: Double, longitude: Double): LatLng {
            val latLng = LatLng(latitude, longitude)
            val converter = com.amap.api.maps2d.CoordinateConverter()
            converter.from(com.amap.api.maps2d.CoordinateConverter.CoordType.GPS)
            converter.coord(latLng)
            return converter.convert()
        }
    
        //圆周率 GCJ_02_To_WGS_84
        var PI = 3.14159265358979324
    
        /**
         * 方法描述:方法可以将高德地图SDK获取到的GPS经纬度转换为真实的经纬度,可以用于解决安卓系统使用高德SDK获取经纬度的转换问题。
         * @param 需要转换的经纬度
         * @return 转换为真实GPS坐标后的经纬度
         * @throws <异常类型> {@inheritDoc} 异常描述
        </异常类型> */
        fun delta(lat: Double, lon: Double): HashMap<String, Double> {
            val a = 6378245.0//克拉索夫斯基椭球参数长半轴a
            val ee = 0.00669342162296594323//克拉索夫斯基椭球参数第一偏心率平方
            var dLat = this.transformLat(lon - 105.0, lat - 35.0)
            var dLon = this.transformLon(lon - 105.0, lat - 35.0)
            val radLat = lat / 180.0 * this.PI
            var magic = Math.sin(radLat)
            magic = 1 - ee * magic * magic
            val sqrtMagic = Math.sqrt(magic)
            dLat = dLat * 180.0 / (a * (1 - ee) / (magic * sqrtMagic) * this.PI)
            dLon = dLon * 180.0 / (a / sqrtMagic * Math.cos(radLat) * this.PI)
    
            val hm = HashMap<String, Double>()
            hm.put("lat", lat - dLat)
            hm.put("lon", lon - dLon)
    
            return hm
        }
    
        //转换经度
        fun transformLon(x: Double, y: Double): Double {
            var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x))
            ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0
            ret += (20.0 * Math.sin(x * this.PI) + 40.0 * Math.sin(x / 3.0 * this.PI)) * 2.0 / 3.0
            ret += (150.0 * Math.sin(x / 12.0 * this.PI) + 300.0 * Math.sin(x / 30.0 * this.PI)) * 2.0 / 3.0
            return ret
        }
    
        //转换纬度
        fun transformLat(x: Double, y: Double): Double {
            var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x))
            ret += (20.0 * Math.sin(6.0 * x * this.PI) + 20.0 * Math.sin(2.0 * x * this.PI)) * 2.0 / 3.0
            ret += (20.0 * Math.sin(y * this.PI) + 40.0 * Math.sin(y / 3.0 * this.PI)) * 2.0 / 3.0
            ret += (160.0 * Math.sin(y / 12.0 * this.PI) + 320 * Math.sin(y * this.PI / 30.0)) * 2.0 / 3.0
            return ret
        }
    }

    高德支持谷歌瓦片
    1.高德地图如果想要显示国外地图,可以选择使用谷歌瓦片

     /**
         * 加载在线瓦片数据
         */
        private fun useOMCMap() {
    //        val url = "http://www.google.cn/maps/vt?lyrs=y&amp;gl=cn&amp;x=%d&amp;s=&amp;y=%d&amp;z=%d"
    //        val url =  "http://mt1.google.cn/vt/lyrs=y&amp;hl=zh-CN&amp;gl=cn&amp;x=%d&amp;s=&amp;y=%d&amp;z=%d"   //3D卫星地图
    //        val url = "http://mt0.google.cn/vt/lyrs=y@198&amp;hl=zh-CN&amp;gl=cn&amp;src=app&amp;x=%d&amp;y=%d&amp;z=%d&amp;s="   //卫星地图
            val url = "http://mt2.google.cn/vt/lyrs=m@167000000&amp;hl=zh-CN&amp;gl=cn&amp;src=app&amp;x=%d&amp;y=%d&amp;z=%d&amp;s=Galil"  //平面地图
            if (tileOverlayOptions == null) {
                tileOverlayOptions = TileOverlayOptions().tileProvider(object : UrlTileProvider(256, 256) {
                    override fun getTileUrl(x: Int, y: Int, zoom: Int): URL? {
                        try {
                            //return new URL(String.format(url, zoom + 1, TileXYToQuadKey(x, y, zoom)));
                            //return new URL(String.format(url, x, y, zoom));
                            val mFileDirName: String
                            val mFileName: String
                            mFileDirName = String.format("L%02d/", zoom + 1)
                            mFileName = String.format("%s", TileXYToQuadKey(x, y, zoom))//为了不在手机的图片中显示,下载的图片取消jpg后缀,文件名自己定义,写入和读取一致即可,由于有自己的bingmap图源服务,所以此处我用的bingmap的文件名
                            val LJ = FileApi.MAP_DIRECTORY + mFileDirName + mFileName
                            if (MapImageCache.instance.isBitmapExit(mFileDirName + mFileName)) {//判断本地是否有图片文件,如果有返回本地url,如果没有,缓存到本地并返回googleurl
                                return URL("file://" + LJ)
                            } else {
                                val filePath = String.format(url, x, y, zoom)
                                val mBitmap: Bitmap
                                //mBitmap = BitmapFactory.decodeStream(getImageStream(filePath));//不知什么原因导致有大量的图片存在坏图,所以重写InputStream写到byte数组方法
                                val stream = getImageStream(filePath)
                                if (stream != null) {
                                    mBitmap = getImageBitmap(stream)
                                    try {
                                        saveFile(mBitmap, mFileName, mFileDirName)
                                    } catch (e: IOException) {
                                        e.printStackTrace()
                                    }
                                }
    
                                return URL(filePath)
                            }
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
    
                        return null
                    }
                })
                tileOverlayOptions!!.diskCacheEnabled(false)   //由于高德自带的瓦片缓存在关闭程序后会自动清空,所以无意义,关闭本地缓存
                        .diskCacheDir(FileApi.MAP_DIRECTORY)
                        .diskCacheSize(1024000)
                        .memoryCacheEnabled(true)
                        .memCacheSize(102400)
                        .zIndex(-9999f)
            }
            //增加瓦片贴图
            mtileOverlay = aMap?.addTileOverlay(tileOverlayOptions)
            mtileOverlay?.isVisible = true
        }

    如果需要在中国地域中停止添加瓦片,需要删除掉瓦片

    fun stopUseOMCMap() {
            mtileOverlay?.remove()
            mtileOverlay?.clearTileCache()
            mtileOverlay?.isVisible = false
            aMap?.removecache()
    
        }

    注意一点,请不要一直触发重复addTileOverlay,调用删除的次数和添加的次数是需要对应的。
    这里还有一部分的代码没有贴上,我将会开放一个模块的演示供大家演示,有兴趣的同学也可以在群内联系我。

    特殊问题

    1.高德只支持国内坐标详情,如果在国外手送,国内手机会使用高德地图,将会无法显示国外坐标详情

    3.地图搜索涉及到异步返回,onDestroy请去掉监听,不然会有内存泄露
    4.暂时发现高德的mapview会持有活动背景对象不释放的情况,修复好会在这里更新
    5.高德搜索的时候,如果不加入城市的参数,将是全国搜索,很可能搜索不到你所在城市的地点(获取定位的时候可以顺带获取所在城市名字),请谨慎处理。

    Android开发高德地图和google地图适配转载http://www.codesocang.com/appboke/38742.html
    标签:网站源码