IM 使用的高德地图和 google 地图适配

1,104 阅读5分钟
原文链接: xiaozhuanlan.com

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

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

Sdk接入

1.google地图,接入相对比较简单,当然因为Android本身就是google亲儿子的原因。
需要引入google service的sdk,以及google map的sdk
developers.google.com/places/andr…

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

预览模块

地图选择UI

地图选择UI

1.高德地图是通过sdk提供的com.amap.api.maps2d.MapView自定义地图View来实现的。
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.地图预览
地图预览(不小心透露了我的工作地址).png

地图预览(不小心透露了我的工作地址).png

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

google地图是使用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)))
        }

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

定位模块

定位预览图.png

定位预览图.png

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?) {
       //监听实时定位
    }

google的定位是使用Android原生的Location定位

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?) {

    }

搜索模块

搜索结果.png

搜索结果.png

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("&radius=").append(radius.toString())
        builder.append("&key=").append(googlePlaceKey)
        return RxVolley.get<GoogleLocation>(builder.toString(), null, object : TypeToken<GoogleLocation>() {}.type)
    }

第二种是文字关联搜索(地点自动完成),google提供了一个自定义的Fragment,但是如果你有高级定制,不用AutoCompleteTextView,那就需要通过定义一个Adapter来获取相关内容。(搜索结果比较少)
这边是使用了需要高级定义搜索,所以使用了Adapter的形式。

    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是支持通过关键字搜索和经纬度地址附近搜索。
```
/**
* 经纬度搜索