1. <div id="f8mbs"></div>
        您好,歡迎來到源碼搜藏網!分享精神,快樂你我!
        [加入VIP] 設為首頁 | 收藏本站 | 網站地圖 | Sitemap | TAG標簽
      2. 首 頁
      3. 在線工具
      4. jquery手冊
      5. 當前位置:首頁 > 安卓源碼 > 技術博客 >

        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/[email protected]&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/[email protected]&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.rhcg.tw/appboke/38742.html
        標簽:網站源碼
        辽宁十一选五单双