地址選擇.gif
1.簡介
實(shí)現(xiàn)從地圖上進(jìn)行地理位置的選擇读第,包含地圖拖動(dòng)停止時(shí)边篮,周邊位置的檢索以及輸入關(guān)鍵字的檢索
2.簡單實(shí)現(xiàn)步驟
2.1 地圖sdk導(dǎo)入
根據(jù)百度官方文檔導(dǎo)入sdk以及相關(guān)初始化操作
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 根據(jù)定位回調(diào)將地圖中心點(diǎn)移動(dòng)到定位的當(dāng)前位置
/**
* 定位回調(diào)
*/
private val locationListener = object : BDAbstractLocationListener() {
override fun onReceiveLocation(p0: BDLocation?) {
p0?.let {
tvLocation.text = it.city
//移動(dòng)地圖中心點(diǎn)到定位的位置
val statusBuilder =
MapStatus.Builder().target(LatLng(it.latitude, it.longitude)).zoom(17f)
baiduMap?.animateMapStatus(MapStatusUpdateFactory.newMapStatus(statusBuilder.build()))
}
}
}
2.4 定位成功后弊仪,poi周邊檢索
使用關(guān)鍵字以及經(jīng)緯度和搜索半徑去檢索周邊
// 周邊搜索
private var poiSearch: PoiSearch = PoiSearch.newInstance()
// 設(shè)置檢索回調(diào)
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, "未找到結(jié)果", 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?) {
}
}
/**
* 根據(jù)經(jīng)緯度進(jìn)行周邊檢索
*/
private fun poiSearch(latLng: LatLng) {
val searchOption = PoiNearbySearchOption().keyword("辦公樓\$商場\$商鋪\$菜場\$批發(fā)市場\$超市\(zhòng)$酒店\$飯店")
.location(latLng)
.radius(1000)
.pageNum(0)
.pageCapacity(20)
.radiusLimit(false)
.scope(2)
poiSearch.searchNearby(searchOption)
}
2.5 移動(dòng)地圖
當(dāng)停止移動(dòng)地圖后汰寓,根據(jù)停止后的經(jīng)緯度去查詢周邊
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 關(guān)鍵字搜索
關(guān)鍵字搜索時(shí)菊碟,根據(jù)輸入的城市以及關(guān)鍵字進(jìn)行poi搜索裕照,監(jiān)聽輸入框的文字實(shí)現(xiàn)即時(shí)搜索
//推薦搜索
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 搜索結(jié)果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, "未找到結(jié)果", 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)
}
/**
* 標(biāo)記點(diǎn)動(dòng)畫
*/
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) // 動(dòng)畫重復(fù)模式
transformation.setRepeatCount(1) // 動(dòng)畫重復(fù)次數(shù)
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
/**
* 定位回調(diào)
*/
private val locationListener = object : BDAbstractLocationListener() {
override fun onReceiveLocation(p0: BDLocation?) {
p0?.let {
tvLocation.text = it.city
//移動(dòng)地圖中心點(diǎn)到定位的位置
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()
}
/**
* 展示標(biāo)記點(diǎn)
*/
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()
}
}
/**
* 根據(jù)經(jīng)緯度進(jìn)行周邊檢索
*/
private fun poiSearch(latLng: LatLng) {
val searchOption = PoiNearbySearchOption().keyword("辦公樓\$商場\$商鋪\$菜場\$批發(fā)市場\$超市\(zhòng)$酒店\$飯店")
.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()
}
}