地址选择.gif
1.简介
实现从地图上进行地理位置的选择,包含地图拖动停止时,周边位置的检索以及输入关键字的检索
2.简单实现步骤
2.1 地图sdk导入
根据百度官方文档导入sdk以及相关初始化操作
2.2 定位
private var locationClient: LocationClient? = null
/**
* 定位
*/
private fun getLocation() {
locationClient = LocationClient(applicationContext)
val locationClientOption = LocationClientOption().apply {
openGps = true
setIsNeedAddress(true)
setIsNeedLocationDescribe(true)
}
locationClient?.locOption = locationClientOption
locationClient?.registerLocationListener(locationListener)
locationClient?.start()
}
2.3 根据定位回调将地图中心点移动到定位的当前位置
/**
* 定位回调
*/
private val locationListener = object : BDAbstractLocationListener() {
override fun onReceiveLocation(p0: BDLocation?) {
p0?.let {
tvLocation.text = it.city
//移动地图中心点到定位的位置
val statusBuilder =
MapStatus.Builder().target(LatLng(it.latitude, it.longitude)).zoom(17f)
baiduMap?.animateMapStatus(MapStatusUpdateFactory.newMapStatus(statusBuilder.build()))
}
}
}
2.4 定位成功后,poi周边检索
使用关键字以及经纬度和搜索半径去检索周边
// 周边搜索
private var poiSearch: PoiSearch = PoiSearch.newInstance()
// 设置检索回调
poiSearch.setOnGetPoiSearchResultListener(poiSearchResultListener)
/**
* 周边poi检索
*/
private var poiSearchResultListener = object : OnGetPoiSearchResultListener {
override fun onGetPoiIndoorResult(p0: PoiIndoorResult?) {
}
override fun onGetPoiResult(result: PoiResult?) {
if (result == null || result.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) {
Toast.makeText(this@ChooseAddressActivity, "未找到结果", Toast.LENGTH_LONG).show()
return
}
if (result.error == SearchResult.ERRORNO.NO_ERROR) {
result.let {
poiResultAdapter.setData(it.allPoi)
}
}
}
override fun onGetPoiDetailResult(p0: PoiDetailResult?) {
}
override fun onGetPoiDetailResult(p0: PoiDetailSearchResult?) {
}
}
/**
* 根据经纬度进行周边检索
*/
private fun poiSearch(latLng: LatLng) {
val searchOption = PoiNearbySearchOption().keyword("办公楼\$商场\$商铺\$菜场\$批发市场\$超市\$酒店\$饭店")
.location(latLng)
.radius(1000)
.pageNum(0)
.pageCapacity(20)
.radiusLimit(false)
.scope(2)
poiSearch.searchNearby(searchOption)
}
2.5 移动地图
当停止移动地图后,根据停止后的经纬度去查询周边
baiduMap?.setOnMapStatusChangeListener(object : BaiduMap.OnMapStatusChangeListener {
override fun onMapStatusChangeStart(p0: MapStatus?) {
}
override fun onMapStatusChangeStart(p0: MapStatus?, p1: Int) {
}
override fun onMapStatusChange(p0: MapStatus?) {
}
override fun onMapStatusChangeFinish(p0: MapStatus?) {
log("change finished")
p0?.let {
poiSearch(it.target)
}
markerIcon?.let {
it.setAnimation(getTransformationPoint())
it.startAnimation()
}
}
})
2.6 关键字搜索
关键字搜索时,根据输入的城市以及关键字进行poi搜索,监听输入框的文字实现即时搜索
//推荐搜索
private val suggestionSearch: SuggestionSearch = SuggestionSearch.newInstance()
suggestionSearch.setOnGetSuggestionResultListener {
if (it.allSuggestions?.isNotEmpty() == true) {
val list = mutableListOf<PoiInfo>()
it.allSuggestions.forEach {
list.add(PoiInfo().apply {
name = it.key
address = it.address
location = it.getPt()
city = it.city
area = it.district
})
}
searchResultAdapter.setData(list)
}
}
etKey.addTextChangedListener(onTextChanged = { text, _, _, _ ->
println("text changed")
if (TextUtils.isEmpty(text)) {
recyclerSearch.visibility = View.GONE
btnSearch.visibility = View.GONE
} else {
recyclerSearch.visibility = View.VISIBLE
btnSearch.visibility = View.VISIBLE
suggestionSearch.requestSuggestion(
SuggestionSearchOption().keyword(text.toString())
.city(tvLocation.text.toString())
)
}
})
3 代码
3.1 Activity 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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".ChooseAddressActivity">
<TextView
android:id="@+id/tv_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startLocation"
android:paddingHorizontal="10dp"
android:text="定位中"
app:layout_constraintBottom_toBottomOf="@id/et_key"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/et_key" />
<EditText
android:id="@+id/et_key"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:height="30dp"
android:background="@drawable/shape_search"
android:drawableStart="@drawable/ic_search"
android:drawablePadding="10dp"
android:hint="请输入您想搜索的位置"
android:paddingStart="20dp"
android:paddingEnd="0dp"
android:textSize="14sp"
app:layout_constraintEnd_toStartOf="@id/tv_cancel_search"
app:layout_constraintStart_toEndOf="@id/tv_location"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginEnd="20dp" />
<TextView
android:id="@+id/tv_cancel_search"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:gravity="center"
android:onClick="cancelSearch"
android:paddingHorizontal="15dp"
android:text="取消"
android:textColor="#7F7F7F"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/et_key"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/et_key"
tools:visibility="visible" />
<com.baidu.mapapi.map.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="280dp"
android:layout_marginTop="5dp"
app:layout_constraintTop_toBottomOf="@id/et_key" />
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="5dp"
android:src="@drawable/ic_relocation"
app:layout_constraintEnd_toEndOf="@id/map"
app:layout_constraintBottom_toBottomOf="@id/map"
android:layout_marginEnd="10dp"
android:onClick="relocation"
android:layout_marginBottom="10dp"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_result"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/map" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_search"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/white"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_key" />
</androidx.constraintlayout.widget.ConstraintLayout>
3.2 搜索结果adapter xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<ImageView
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="15dp"
android:contentDescription="@string/app_name"
android:src="@drawable/shape_circle" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:paddingStart="0dp"
android:paddingEnd="20dp"
android:textColor="#333333"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_address_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:paddingStart="0dp"
android:paddingEnd="20dp"
android:textColor="#7F7F7F"
android:textSize="14sp" />
</LinearLayout>
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="40dp"
android:layout_marginTop="8dp"
android:background="#CECECE" />
</LinearLayout>
3.3 适配器代码
package com.li.myapplication;
import android.content.Context;
import android.location.GnssMeasurementsEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.baidu.mapapi.search.core.PoiInfo;
import java.util.List;
public class PoiResultAdapter extends RecyclerView.Adapter<PoiResultAdapter.PoiResultHolder> {
List<PoiInfo> poiInfoList;
Context context;
OnAddressChooseListener onAddressChooseListener;
public PoiResultAdapter(Context context, OnAddressChooseListener onAddressChooseListener) {
this.context = context;
this.onAddressChooseListener = onAddressChooseListener;
}
@NonNull
@Override
public PoiResultHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.layout_poi_result, parent, false);
PoiResultHolder holder = new PoiResultHolder(view);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onAddressChooseListener != null) {
onAddressChooseListener.onAddressChoose(poiInfoList.get(holder.getBindingAdapterPosition()));
}
}
});
return holder;
}
@Override
public void onBindViewHolder(@NonNull PoiResultHolder holder, int position) {
PoiInfo poiInfo = poiInfoList.get(position);
holder.tvAddress.setText(poiInfo.name);
holder.tvAddressDetail.setText(poiInfo.address);
}
@Override
public int getItemCount() {
return poiInfoList == null ? 0 : poiInfoList.size();
}
public void setData(List<PoiInfo> list) {
this.poiInfoList = list;
notifyDataSetChanged();
}
static class PoiResultHolder extends RecyclerView.ViewHolder {
TextView tvAddress;
TextView tvAddressDetail;
public PoiResultHolder(@NonNull View itemView) {
super(itemView);
tvAddress = itemView.findViewById(R.id.tv_address);
tvAddressDetail = itemView.findViewById(R.id.tv_address_detail);
}
}
interface OnAddressChooseListener {
void onAddressChoose(PoiInfo poiInfo);
}
}
3.4 Activity代码
package com.li.myapplication
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.graphics.Point
import android.graphics.Rect
import android.os.Bundle
import android.os.Handler
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.view.ViewTreeObserver
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.baidu.location.BDAbstractLocationListener
import com.baidu.location.BDLocation
import com.baidu.location.LocationClient
import com.baidu.location.LocationClientOption
import com.baidu.mapapi.animation.Animation
import com.baidu.mapapi.animation.Transformation
import com.baidu.mapapi.map.*
import com.baidu.mapapi.model.LatLng
import com.baidu.mapapi.search.core.PoiInfo
import com.baidu.mapapi.search.core.SearchResult
import com.baidu.mapapi.search.poi.*
import com.baidu.mapapi.search.sug.SuggestionSearch
import com.baidu.mapapi.search.sug.SuggestionSearchOption
import com.baidu.mapapi.utils.CoordinateConverter
/**
* Author Li
* Date 5/26/21
* Des 选择地址页面
*/
class ChooseAddressActivity : AppCompatActivity(), PoiResultAdapter.OnAddressChooseListener {
private val permissions = arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
private var mapView: MapView? = null
private var baiduMap: BaiduMap? = null
private var mScreenCenterPoint: Point? = null
private var markerIcon: Marker? = null
private val bitmapIcon = BitmapDescriptorFactory.fromResource(R.drawable.ic_location_pin)
private lateinit var tvLocation: TextView
private lateinit var recyclerResult: RecyclerView
private lateinit var poiResultAdapter: PoiResultAdapter
private lateinit var searchResultAdapter: PoiResultAdapter
private lateinit var etKey: EditText
private lateinit var recyclerSearch: RecyclerView
private lateinit var btnSearch: TextView
// 周边搜索
private var poiSearch: PoiSearch = PoiSearch.newInstance()
//推荐搜索
private val suggestionSearch: SuggestionSearch = SuggestionSearch.newInstance()
companion object {
const val RC_PERMISSION = 100
}
/**
* 周边poi检索
*/
private var poiSearchResultListener = object : OnGetPoiSearchResultListener {
override fun onGetPoiIndoorResult(p0: PoiIndoorResult?) {
}
override fun onGetPoiResult(result: PoiResult?) {
if (result == null || result.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) {
Toast.makeText(this@ChooseAddressActivity, "未找到结果", Toast.LENGTH_LONG).show()
return
}
if (result.error == SearchResult.ERRORNO.NO_ERROR) {
result.let {
poiResultAdapter.setData(it.allPoi)
}
}
}
override fun onGetPoiDetailResult(p0: PoiDetailResult?) {
}
override fun onGetPoiDetailResult(p0: PoiDetailSearchResult?) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_choose_address)
mapView = findViewById(R.id.map)
recyclerResult = findViewById(R.id.recycler_result)
tvLocation = findViewById(R.id.tv_location)
etKey = findViewById(R.id.et_key)
recyclerSearch = findViewById(R.id.recycler_search)
btnSearch = findViewById(R.id.tv_cancel_search)
recyclerResult.layoutManager = LinearLayoutManager(this)
poiResultAdapter = PoiResultAdapter(this, this)
recyclerResult.adapter = poiResultAdapter
recyclerSearch.layoutManager = LinearLayoutManager(this)
searchResultAdapter = PoiResultAdapter(this, this)
recyclerSearch.adapter = searchResultAdapter
baiduMap = mapView?.map
baiduMap?.isMyLocationEnabled = true
poiSearch.setOnGetPoiSearchResultListener(poiSearchResultListener)
suggestionSearch.setOnGetSuggestionResultListener {
if (it.allSuggestions?.isNotEmpty() == true) {
val list = mutableListOf<PoiInfo>()
it.allSuggestions.forEach {
list.add(PoiInfo().apply {
name = it.key
address = it.address
location = it.getPt()
city = it.city
area = it.district
})
}
searchResultAdapter.setData(list)
}
}
etKey.addTextChangedListener(onTextChanged = { text, _, _, _ ->
println("text changed")
if (TextUtils.isEmpty(text)) {
recyclerSearch.visibility = View.GONE
btnSearch.visibility = View.GONE
} else {
recyclerSearch.visibility = View.VISIBLE
btnSearch.visibility = View.VISIBLE
suggestionSearch.requestSuggestion(
SuggestionSearchOption().keyword(text.toString())
.city(tvLocation.text.toString())
)
}
})
baiduMap?.setOnMapLoadedCallback {
showMarker()
}
baiduMap?.setOnMapStatusChangeListener(object : BaiduMap.OnMapStatusChangeListener {
override fun onMapStatusChangeStart(p0: MapStatus?) {
}
override fun onMapStatusChangeStart(p0: MapStatus?, p1: Int) {
}
override fun onMapStatusChange(p0: MapStatus?) {
}
override fun onMapStatusChangeFinish(p0: MapStatus?) {
log("change finished")
p0?.let {
poiSearch(it.target)
}
markerIcon?.let {
it.setAnimation(getTransformationPoint())
it.startAnimation()
}
}
})
mapView?.showZoomControls(false)
ActivityCompat.requestPermissions(this, permissions, RC_PERMISSION)
}
/**
* 标记点动画
*/
private fun getTransformationPoint(): Animation? {
if (null != mScreenCenterPoint) {
val pointTo =
Point(mScreenCenterPoint!!.x, mScreenCenterPoint!!.y - 20)
val transformation =
Transformation(
mScreenCenterPoint,
pointTo,
mScreenCenterPoint
)
transformation.setDuration(400)
transformation.setRepeatMode(Animation.RepeatMode.RESTART) // 动画重复模式
transformation.setRepeatCount(1) // 动画重复次数
transformation.setAnimationListener(object :
Animation.AnimationListener {
override fun onAnimationStart() {}
override fun onAnimationEnd() {}
override fun onAnimationCancel() {}
override fun onAnimationRepeat() {}
})
return transformation
}
return null
}
override fun onResume() {
super.onResume()
mapView?.onResume()
}
override fun onPause() {
super.onPause()
mapView?.onPause()
}
private var locationClient: LocationClient? = null
/**
* 定位回调
*/
private val locationListener = object : BDAbstractLocationListener() {
override fun onReceiveLocation(p0: BDLocation?) {
p0?.let {
tvLocation.text = it.city
//移动地图中心点到定位的位置
val statusBuilder =
MapStatus.Builder().target(LatLng(it.latitude, it.longitude)).zoom(17f)
baiduMap?.animateMapStatus(MapStatusUpdateFactory.newMapStatus(statusBuilder.build()))
}
}
}
/**
* 定位
*/
private fun getLocation() {
locationClient = LocationClient(applicationContext)
val locationClientOption = LocationClientOption().apply {
openGps = true
setIsNeedAddress(true)
setIsNeedLocationDescribe(true)
}
locationClient?.locOption = locationClientOption
locationClient?.registerLocationListener(locationListener)
locationClient?.start()
}
/**
* 展示标记点
*/
private fun showMarker() {
baiduMap?.let {
val latLng = it.mapStatus.target
mScreenCenterPoint = it.projection.toScreenLocation(latLng)
val markerOptions = MarkerOptions().position(latLng).icon(bitmapIcon).perspective(true)
.fixedScreenPosition(mScreenCenterPoint)
markerIcon = it.addOverlay(markerOptions) as Marker
}
}
private fun log(msg: Any?) {
Log.e("tag", "log: $msg")
}
override fun onDestroy() {
super.onDestroy()
bitmapIcon.recycle()
poiSearch.destroy()
suggestionSearch.destroy()
mapView?.onDestroy()
locationClient?.unRegisterLocationListener(locationListener)
baiduMap?.clear()
}
fun startLocation(view: View) {
getLocation()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == RC_PERMISSION) {
getLocation()
}
}
/**
* 根据经纬度进行周边检索
*/
private fun poiSearch(latLng: LatLng) {
val searchOption = PoiNearbySearchOption().keyword("办公楼\$商场\$商铺\$菜场\$批发市场\$超市\$酒店\$饭店")
.location(latLng)
.radius(1000)
.pageNum(0)
.pageCapacity(20)
.radiusLimit(false)
.scope(2)
poiSearch.searchNearby(searchOption)
}
fun cancelSearch(view: View) {
etKey.setText("")
}
/**
* 重新定位
*/
fun relocation(view: View) {
getLocation()
}
override fun onAddressChoose(poiInfo: PoiInfo?) {
log(poiInfo)
// setResult(Activity.RESULT_OK, Intent().apply { putExtra("result", poiInfo) })
// finish()
}
}