腾讯地图实现点击搜索地址功能

一、如图。


 

 

 二、设计详情:地图为腾讯MapView、搜索列表RecycleView 为屏幕长度的一半、点击地图空白处关闭列表、点击地图地点名称、搜索并展示;


三、注意点:腾讯地图显示和定位是分开的、上面的效果实现、必须接入地图和定位两个sdk;

//    腾讯定位implementation 'com.tencent.map.geolocation:TencentLocationSdk-openplatform:7.3.0'
//    腾讯地图implementation 'com.tencent.map:tencent-map-vector-sdk:4.3.4'

注意腾讯地图分为2D、3D; 如上为3D效果


四、业务或逻辑关键点 (整体代码在第五点)


地图展示:SupportMapFragment (腾讯提供)、内部封装了MapView、优点:开发者不需要关心地图生命周期;

xml中:

<fragmentandroid:id="@+id/map_frag"class="com.tencent.tencentmap.mapsdk.maps.SupportMapFragment"android:layout_width="match_parent"android:layout_height="match_parent"/>

地址搜索关键代码:

   companion object {private const val MSG_SUGGESTION = 10000}//地址模糊搜索、private fun searchAddress(address: String?) {if (address.isNullOrEmpty()) returnval tencentSearch = TencentSearch(this)val suggestionParam = SuggestionParam(address, "上海")//suggestion也提供了filter()方法和region方法//具体说明见文档,或者官网的webservice对应接口tencentSearch.suggestion(suggestionParam, object : HttpResponseListener<BaseObject> {override fun onSuccess(arg0: Int, arg1: BaseObject?) {if (arg1 == null) {return}progressBar.postDelayed({progressBar.visibility = View.GONEval msg = Message()msg.what = MSG_SUGGESTION//关键点arg1msg.obj = arg1handler.sendMessage(msg)}, 350)}override fun onFailure(arg0: Int, arg1: String?, arg2: Throwable?) {progressBar.visibility = View.GONE}})}//地址搜索结果预览private val handler = object : Handler() {override fun handleMessage(msg: Message) {super.handleMessage(msg)if (msg.what == MSG_SUGGESTION) {val searchList = (msg.obj as SuggestionResultObject).data//RecycViewAdaptermapAdapter.addData(searchList)}}}

搜索结果实体类(腾讯)

public class SuggestionResultObject extends BaseObject {public int count;//此处data 为搜索列表结果数据public List<SuggestionResultObject.SuggestionData> data;//sub_pois 暂无用public List<SuggestionResultObject.SubPoi> sub_pois;public SuggestionResultObject() {}public static final class SubPoi extends JsonComposer {public String parent_id;public String id;public String title;public String address;@Json(name = "location")public LatLng latLng;public String adcode;public String city;public SubPoi() {}}public static final class SuggestionData extends JsonComposer {public String id;public String title;public String address;public String province;public String city;/** @deprecated */public String district;//区号public String adcode;public int type;@Json(name = "location")//经纬度public LatLng latLng;public float _distance;public SuggestionData() {}}
}

地图默认marker点 定位Icon(红色箭头):

  /*** 设置定位图标样式*/private fun setLocMarkerStyle() {val locationStyle = MyLocationStyle()//创建图标 val bitmapDescriptor =BitmapDescriptorFactory.fromResource(R.drawable.ic_map_location)locationStyle?.icon(bitmapDescriptor)//设置定位圆形区域的边框宽度locationStyle?.strokeWidth(3)//设置圆区域的颜色locationStyle?.fillColor(R.color.style)//连续定位,但不会移动到地图中心点,并且会跟随设备移动
//        locationStyle = //locationStyle?.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER)supportMapFragment = fragmentManger.findFragmentById(R.id.map_frag) as SupportMapFragmentval tencentMap = supportMapFragment?.maptencentMap?.setMyLocationStyle(locationStyle)}

点击地址Marker Icon显示:


//地图位置点击回调 、 tencentMap?.setOnMapPoiClickListener(this)override fun onClicked(mapPoi: MapPoi?) {setMarker(mapPoi?.getPosition(), mapPoi?.getName())  }// latLng 经纬度、name 地址名称
private fun setMarker(latLng: LatLng?, name: String?) {marker?.remove()val options = MarkerOptions().position(latLng)//设置infowindowoptions.title("地址:")options.snippet(name)
//        options.icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_launcher))marker = tencentMap?.addMarker(options)marker?.showInfoWindow()}

 移动地图

 //移动镜头private fun initCamera(isAnima: Boolean = false) {//对地图操作类进行操作val cameraSigma = CameraUpdateFactory.newCameraPosition(CameraPosition(centerLatLng,17f,0f,0f))//移动地图if (isAnima)tencentMap?.animateCamera(cameraSigma)elsetencentMap?.moveCamera(cameraSigma)}

五、整体代码


class MapActivity : BaseHttpActivity(), LocationSource, TencentLocationListener,TencentMap.OnMapPoiClickListener {companion object {private const val MSG_SUGGESTION = 10000}//腾讯地图操控类private var tencentMap: TencentMap? = nullprivate var supportMapFragment: SupportMapFragment? = null//UI控制类private var mapUiSettings: UiSettings? = null//地址数据private var locationChangedListener: OnLocationChangedListener? = null//位置管理private var locationManager: TencentLocationManager? = null//地址请求封装private var locationRequest: TencentLocationRequest? = null//地图图标样式private var locationStyle: MyLocationStyle? = null//地图标点private var marker: Marker? = null//默认北京private var centerLatLng = LatLng(39.984066, 116.307548)//适配器相关private val mapAdapter = MapAdapter()//------------BaseHttpActivity 重载的方法 无需关注、删除即可override fun statusBarColor() = R.color.F7F7F7override fun needRightTv() = trueoverride fun needBackIcon() = trueoverride fun getLayoutId() = R.layout.map_activityoverride fun setTitle() = getString(R.string.map_title)//------------BaseHttpActivity 重载的方法、删除即可private var firstInter = true//---等同于onCreat() 优先级大于initDate()override fun initView() {//tvRight.text = getString(R.string.map_sub_title)//tvRight.setTextColor(getColor(R.color.F15A24))//创建tencentMap地图对象,可以完成对地图的几乎所有操作val fragmentManger = supportFragmentManagersupportMapFragment = fragmentManger.findFragmentById(R.id.map_frag) as SupportMapFragmentinitMap()//初始化CamerainitCamera()//建立定位initLocation()//初始化地图点击处理initPoiClick()initAdapter()}private fun initAdapter() {addressRv.layoutManager = LinearLayoutManager(this)addressRv.adapter = mapAdapter}//---等同于onCreat() 优先级小于initView()override fun initDate() {ivReset.setOnClickListener {initCamera(true)}// 地址选择后的点击事件、可以参考/*tvRight.setOnClickListener {val data = mapAdapter.getSelectData()if (data != null) {eventBus.post(data)finish()} else {ToastUtils.showToast("所选地址不能为空!")}}*/}//移动镜头private fun initCamera(isAnima: Boolean = false) {//对地图操作类进行操作val cameraSigma = CameraUpdateFactory.newCameraPosition(CameraPosition(centerLatLng,17f,0f,0f))//移动地图if (isAnima)tencentMap?.animateCamera(cameraSigma)elsetencentMap?.moveCamera(cameraSigma)}private fun initMap() {tencentMap = supportMapFragment?.map//设置当前位置可见tencentMap?.isMyLocationEnabled = true//地图UI设置mapUiSettings = tencentMap?.uiSettings//设置显示定位的图标
//        mapUiSettings?.isMyLocationButtonEnabled = truemapUiSettings?.setLogoPosition(TencentMapOptions.LOGO_POSITION_TOP_RIGHT)//地图点击监听tencentMap?.setOnMapClickListener {progressBar?.visibility = View.GONEaddressRv?.visibility = View.GONEflContainer.reSetHeight()}}/*** 定位的一些初始化设置*/private fun initLocation() {//用于访问腾讯定位服务的类, 周期性向客户端提供位置更新locationManager = TencentLocationManager.getInstance(this)//设置坐标系locationManager?.coordinateType = TencentLocationManager.COORDINATE_TYPE_GCJ02//创建定位请求locationRequest = TencentLocationRequest.create()//设置定位周期(位置监听器回调周期)为3slocationRequest?.interval = 3000//地图上设置定位数据源tencentMap?.setLocationSource(this)//设置当前位置可见tencentMap?.isMyLocationEnabled = true//设置定位图标样式setLocMarkerStyle()tencentMap?.setMyLocationStyle(locationStyle)}/*** 设置定位图标样式*/private fun setLocMarkerStyle() {locationStyle = MyLocationStyle()//创建图标val bitmapDescriptor =
//            BitmapDescriptorFactory.fromBitmap(getBitMap(R.drawable.ic_map_location))BitmapDescriptorFactory.fromResource(R.drawable.ic_map_location)locationStyle?.icon(bitmapDescriptor)//设置定位圆形区域的边框宽度locationStyle?.strokeWidth(3)//设置圆区域的颜色locationStyle?.fillColor(R.color.style)//连续定位,但不会移动到地图中心点,并且会跟随设备移动
//        locationStyle = locationStyle?.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER)}//设置定位图片private fun getBitMap(resourceId: Int): Bitmap? {var bitmap = BitmapFactory.decodeResource(resources, resourceId)val width = bitmap.widthval height = bitmap.heightval newWidth = 55val newHeight = 55val widthScale = newWidth.toFloat() / widthval heightScale = newHeight.toFloat() / heightval matrix = Matrix()matrix.postScale(widthScale, heightScale)bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true)return bitmap}//地图位置点击private fun initPoiClick() {tencentMap?.setOnMapPoiClickListener(this)}//首次启动验证override fun activate(onLocationChangedListener: OnLocationChangedListener?) {locationChangedListener = onLocationChangedListenerval err = locationManager?.requestLocationUpdates(locationRequest, this, Looper.myLooper())when (err) {1 -> Toast.makeText(this, "设备缺少使用腾讯定位服务需要的基本条件", Toast.LENGTH_SHORT).show()2 -> Toast.makeText(this, "manifest 中配置的 key 不正确", Toast.LENGTH_SHORT).show()3 -> Toast.makeText(this, "自动加载libtencentloc.so失败", Toast.LENGTH_SHORT).show()else -> {}}}override fun deactivate() {locationManager?.removeUpdates(this)locationManager = nulllocationRequest = nulllocationChangedListener = null}//定位回调override fun onLocationChanged(tencentLocation: TencentLocation?,error: Int,errorMsg: String?) {if (error == TencentLocation.ERROR_OK && locationChangedListener != null) {if (tencentLocation != null) {val location = Location(tencentLocation.provider)//设置经纬度以及精度location.latitude = tencentLocation.latitudelocation.longitude = tencentLocation.longitudelocation.accuracy = tencentLocation.accuracylocationChangedListener?.onLocationChanged(location)//记录坐标centerLatLng.setLatitude(tencentLocation.latitude)centerLatLng.setLongitude(tencentLocation.longitude)//设置镜头不随中心点移动if (locationStyle?.myLocationType != MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER) {locationStyle =locationStyle?.myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER)tencentMap?.setMyLocationStyle(locationStyle)}//地址搜索if (firstInter) {firstInter = falsesearchAddress(tencentLocation.name)}} else {}}}override fun onStatusUpdate(p0: String?, p1: Int, p2: String?) {}override fun onClicked(mapPoi: MapPoi?) {setMarker(mapPoi?.getPosition(), mapPoi?.getName())//searchAddress loadingprogressBar.visibility = View.VISIBLEsearchAddress(mapPoi?.getName())}private fun setMarker(latLng: LatLng?, name: String?) {marker?.remove()val options = MarkerOptions().position(latLng)//设置infowindowoptions.title("地址:")options.snippet(name)
//        options.icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_launcher))marker = tencentMap?.addMarker(options)marker?.showInfoWindow()}//模糊搜索private fun searchAddress(address: String?) {if (address.isNullOrEmpty()) returnval tencentSearch = TencentSearch(this)val suggestionParam = SuggestionParam(address, "上海")//suggestion也提供了filter()方法和region方法//具体说明见文档,或者官网的webservice对应接口tencentSearch.suggestion(suggestionParam, object : HttpResponseListener<BaseObject> {override fun onSuccess(arg0: Int, arg1: BaseObject?) {if (arg1 == null) {return}progressBar.postDelayed({progressBar.visibility = View.GONEval msg = Message()msg.what = MSG_SUGGESTIONmsg.obj = arg1handler.sendMessage(msg)}, 350)}override fun onFailure(arg0: Int, arg1: String?, arg2: Throwable?) {progressBar.visibility = View.GONE}})}//地址搜索结果预览private val handler = object : Handler() {override fun handleMessage(msg: Message) {super.handleMessage(msg)if (msg.what == MSG_SUGGESTION) {val searchList = (msg.obj as SuggestionResultObject).datamapAdapter.addData(searchList)if (searchList.isEmpty()) {addressRv.visibility = View.GONEflContainer.reSetHeight()} else {addressRv.visibility = View.VISIBLE//动态设置布局宽高val heightDp = ScreenUtils.getAppScreenHeight() / 2flContainer.setHeight(heightDp)}}}}override fun onDestroy() {super.onDestroy()handler.removeCallbacksAndMessages(null)}}

map_activity.xml

<androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><fragmentandroid:id="@+id/map_frag"class="com.tencent.tencentmap.mapsdk.maps.SupportMapFragment"android:layout_width="match_parent"android:layout_height="0dp"app:layout_constraintBottom_toTopOf="@+id/flContainer"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><FrameLayoutandroid:id="@+id/flContainer"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:background="@color/white"android:animateLayoutChanges="true"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/addressRv"android:layout_width="match_parent"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"tools:visibility="visible" /><ProgressBarandroid:id="@+id/progressBar"android:layout_width="20dp"android:layout_height="20dp"android:layout_gravity="center|top"android:layout_margin="5dp"android:indeterminateTint="@color/F15A24"android:visibility="gone"tools:visibility="visible" /></FrameLayout><ImageViewandroid:id="@+id/ivReset"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|end"android:layout_marginEnd="15dp"android:layout_marginBottom="15dp"android:src="@drawable/ic_location_reset"android:text="reSet"app:layout_constraintBottom_toTopOf="@+id/flContainer"app:layout_constraintEnd_toEndOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

资源icon 

ic_location_reset

ic_map_location

颜色: android:indeterminateTint="@color/F15A24"   ==#F15A24、

android:background="@color/white"==#ffffff

 地图搜索列表 适配器代码及相关xml

class MapAdapter : RecyclerView.Adapter<MapAdapter.MapHolder>() {private val data = mutableListOf<SuggestionResultObject.SuggestionData>()private var selectPos = 0override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MapHolder {val view =LayoutInflater.from(parent.context).inflate(R.layout.adapter_map_item, parent, false)return MapHolder(view)}override fun onBindViewHolder(holder: MapHolder, position: Int) {holder.bind(position)}override fun getItemCount() = data.sizefun addData(newData: MutableList<SuggestionResultObject.SuggestionData>?) {newData?.run {if (newData.isNotEmpty()) {selectPos = 0data.clear()data.addAll(this)notifyDataSetChanged()}}}inner class MapHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {fun bind(pos: Int) {val bean = data[pos]with(itemView) {val leftImg = findViewById<ImageView>(R.id.ivAddress)//地址标题val tvAddressTitle = findViewById<TextView>(R.id.tvAddressTitle)//地址详情val tvAddressInfo = findViewById<TextView>(R.id.tvAddressInfo)val bomLine = findViewById<View>(R.id.bom_line)tvAddressTitle.text = bean.titletvAddressInfo.text = bean.addressif (selectPos == pos) {tvAddressTitle.setTextColor(ContextCompat.getColor(itemView.context,R.color.ED6A0C))} else {tvAddressTitle.setTextColor(ContextCompat.getColor(itemView.context,R.color._666666))}if (pos == 0) {leftImg.setImageResource(R.drawable.ic_location)} else {leftImg.setImageResource(R.drawable.ic_map_search_point)}//隐藏末尾下划线if (pos == data.size - 1) {bomLine.visibility = View.GONE} else bomLine.visibility = View.VISIBLE//状态改变setOnClickListener {if (selectPos == bindingAdapterPosition) return@setOnClickListenerselectPos = bindingAdapterPositionnotifyDataSetChanged()}}}}fun getSelectData(): SuggestionResultObject.SuggestionData? = try {data[selectPos]} catch (e: Exception) {e.printStackTrace()null}}
adapter_map_item.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:background="@color/white"android:layout_width="match_parent"android:layout_height="72dp"><ImageViewandroid:id="@+id/ivAddress"android:layout_width="15dp"android:layout_height="20dp"android:layout_marginLeft="15dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent"tools:src="@drawable/ic_location" /><TextViewandroid:id="@+id/tvAddressTitle"style="@style/tv_18_color_66_font_m"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginLeft="10dp"android:layout_marginTop="12dp"android:ellipsize="end"android:includeFontPadding="false"android:maxLines="1"android:paddingRight="5dp"app:layout_constraintLeft_toRightOf="@+id/ivAddress"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"tools:text="翔鹿大厦" /><TextViewandroid:id="@+id/tvAddressInfo"style="@style/tv_14_color_aa_font_m"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginTop="2dp"android:ellipsize="end"android:includeFontPadding="false"android:maxLines="1"android:paddingRight="5dp"app:layout_constraintLeft_toLeftOf="@+id/tvAddressTitle"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@+id/tvAddressTitle"tools:text="上海市闵行区莘庄镇顾戴路3009号、上海市闵行区莘庄镇顾戴路3009号、上海市闵行区莘庄镇顾戴路3009号、上海市闵行区莘庄镇顾戴路3009号" /><Viewandroid:id="@+id/bom_line"android:layout_width="0dp"android:layout_height="1dp"android:layout_marginLeft="15dp"android:layout_marginRight="15dp"android:background="@color/F4F4F4"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

资源或icon

ic_location

   

ic_map_search_point

android:background="@color/F4F4F4"   == #f4f4f4
R.color.ED6A0C ==#ED6A0C

字体先关样式可删除


至此Over!!!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平