You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

574 lines
18 KiB

package com.gyf.csams.activity.model
import android.app.Activity
import android.app.Application
import android.content.Context
import android.graphics.BitmapFactory
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.baidu.mapapi.map.*
import com.baidu.mapapi.model.LatLng
import com.baidu.mapapi.model.LatLngBounds
import com.baidu.mapapi.search.core.PoiInfo
import com.baidu.mapapi.search.core.SearchResult
import com.baidu.mapapi.search.geocode.*
import com.baidu.mapapi.search.poi.*
import com.baidu.mapapi.search.sug.SuggestionResult
import com.baidu.mapapi.search.sug.SuggestionSearch
import com.baidu.mapapi.search.sug.SuggestionSearchOption
import com.gyf.csams.MyLocationListener
import com.gyf.csams.NOT_IMPL_TIP
import com.gyf.csams.R
import com.gyf.csams.util.ContextUtil
import com.gyf.lib.uikit.ScaffoldModel
import com.gyf.lib.uikit.StringForm
import com.gyf.lib.util.DATETIME_FORMAT
import com.orhanobut.logger.Logger
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.util.*
class SizeForm(val application: Application) :
StringForm(formDesc = application.getString(R.string.activity_size), textLength = 2) {
val _formError = MutableLiveData<String>()
val formError: LiveData<String> = _formError
val maxActivitySize = application.resources.getInteger(R.integer.activity_size)
val rangeError = application.getString(R.string.activity_size_error, 1, maxActivitySize)
override fun onChange(value: String) {
if (value.length > textLength) {
_formError.value = "${formDesc}不能超过最大长度$textLength"
_formValue.value = value.slice(IntRange(0, textLength - 1))
} else if (value.matches(Regex("\\d+")) && value.toInt() !in 1..maxActivitySize) {
Logger.i("活动人数:${value}不合法")
_formError.value = rangeError
} else {
_formValue.value = value
_formError.value = ""
}
}
}
/**
* 申请活动数据状态管理
*
* @constructor
*
*
* @param application
*/
class ApplyActViewModel(application: Application) : AndroidViewModel(application) {
val activityName =
StringForm(formDesc = application.getString(R.string.activity_name), textLength = 10)
val activityTime = StringForm(
formDesc = application.getString(R.string.activity_time),
textLength = DATETIME_FORMAT.length
)
val activityAddress =
StringForm(formDesc = application.getString(R.string.activity_address), textLength = 30)
val activityDesc =
StringForm(formDesc = application.getString(R.string.activity_desc), textLength = 50)
val activitySize = SizeForm(application = application)
val city =
object : StringForm(formDesc = application.getString(R.string.city), textLength = 4) {
override val formPlaceholder = ""
}
// 默认逆地理编码半径范围
private val sDefaultRGCRadius = 500
private val mGeoCoder: GeoCoder = GeoCoder.newInstance().apply {
setOnGetGeoCodeResultListener(object : OnGetGeoCoderResultListener {
override fun onGetGeoCodeResult(p0: GeoCodeResult?) {
}
override fun onGetReverseGeoCodeResult(reverseGeoCodeResult: ReverseGeoCodeResult?) {
reverseGeoCodeResult?.let {
GlobalScope.launch {
updateUI(it)
}
}
}
})
}
lateinit var scaffoldModel: ScaffoldModel
private val _mapView = MutableLiveData<MapView>()
val mapView: LiveData<MapView> = _mapView
/**
* 更新UI
*
* @param reverseGeoCodeResult
*/
private fun updateUI(reverseGeoCodeResult: ReverseGeoCodeResult) {
Logger.i("逆编码更新地点信息")
var poiInfo = reverseGeoCodeResult.poiList
val curAddressPoiInfo = PoiInfo()
curAddressPoiInfo.address = reverseGeoCodeResult.address
curAddressPoiInfo.location = reverseGeoCodeResult.location
if (null == poiInfo) {
poiInfo = ArrayList(2)
}
poiInfo.add(0, curAddressPoiInfo)
_poInfo.postValue(poiInfo)
_selectPoi.postValue(null)
}
fun searchPoiInCity() {
if (city.formValue.value?.isEmpty() == true ||
address.formValue.value?.isEmpty() == true
) {
Logger.i("city=${city.formValue.value},keyword=${address.formValue.value}")
return
} else {
Logger.i("${city.formValue.value}市内找${address.formValue.value}")
mPoiSearch.searchInCity(
PoiCitySearchOption()
.city(city.formValue.value)
.keyword(address.formValue.value)
.pageNum(0) // 分页编号
.cityLimit(true)
.scope(1)
)
}
}
/**
* 在地图上定位poi
*
* @param suggestInfo
*/
fun locateSuggestPoi(suggestInfo: SuggestionResult.SuggestionInfo) {
// _sugResult.value?.clear()
_sugResult.value = mutableListOf()
_mapView.value?.apply {
val latLng = suggestInfo.getPt()
_mStatusChangeByItemClick.value = false
// 将地图平移到 latLng 位置
val mapStatusUpdate = MapStatusUpdateFactory.newLatLng(latLng)
map.setMapStatus(mapStatusUpdate)
// 清除之前的
// clearData()
// 显示当前的
// if (!showSuggestMarker(latLng)) {
// searchPoiInCity()
// }
}
}
/**
* 显示定位点
*
* @param latLng
*/
private fun showSuggestMarker(latLng: LatLng?): Boolean {
if (null == latLng) {
return false
}
_mapView.value?.apply {
val markerOptions = MarkerOptions()
.position(latLng)
.icon(mBitmapDescWaterDrop)
.scaleX(1.5f)
.scaleY(1.5f)
.clickable(true)
map.addOverlay(markerOptions)
return true
}
return false
}
val mSuggestionSearch: SuggestionSearch = SuggestionSearch.newInstance().apply {
setOnGetSuggestionResultListener { it ->
if (it == null
|| it.error == SearchResult.ERRORNO.RESULT_NOT_FOUND
) {
scaffoldModel.update(
message = application.getString(
R.string.search_null
)
)
Logger.i("sug检索不到地点")
} else {
it.allSuggestions?.let {
Logger.i("sug检索到${it.size}个地点")
_sugResult.value = it
}
}
}
}
private val mPoiSearch: PoiSearch = PoiSearch.newInstance().apply {
setOnGetPoiSearchResultListener(
object : OnGetPoiSearchResultListener {
override fun onGetPoiResult(
poiResult: PoiResult?
) {
if (poiResult == null || poiResult.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) {
scaffoldModel.update(
message = application.getString(
R.string.search_null
)
)
Logger.i("Poi检索不到地点")
} else {
poiResult.allPoi?.let {
Logger.i("Poi检索到${it.size}个地点")
setPoiResult(it)
}
}
}
@Suppress("DEPRECATION")
override fun onGetPoiDetailResult(p0: PoiDetailResult?) {
Logger.i("$p0")
}
override fun onGetPoiDetailResult(p0: PoiDetailSearchResult?) {
Logger.i("$p0")
}
override fun onGetPoiIndoorResult(p0: PoiIndoorResult?) {
Logger.i("$p0")
}
})
}
private val mBitmapDescWaterDrop = BitmapDescriptorFactory.fromBitmap(
BitmapFactory.decodeResource(
application.resources,
R.drawable.icon_binding_point
)
)
private val mMarkerPoiInfo = mutableMapOf<Marker, PoiInfo>()
private var mPreSelectMarker: Marker? = null
private fun showPoiMarker(poiInfo: PoiInfo?, i: Int) {
poiInfo?.apply {
_mapView.value?.apply {
val markerOptions = MarkerOptions()
.position(location)
.icon(mBitmapDescWaterDrop)
// 第一个poi放大显示
// if (0 == i) {
// val infoWindow: InfoWindow = getPoiInfoWindow(poiInfo)
// markerOptions.scaleX(1.5f).scaleY(1.5f).infoWindow(infoWindow)
// }
val marker = map.addOverlay(markerOptions) as Marker
mMarkerPoiInfo[marker] = poiInfo
if (0 == i) {
mPreSelectMarker = marker
}
}
}
}
private fun setPoiResult(poiInfos: List<PoiInfo>?) {
_mapView.value?.apply {
if (null == poiInfos || poiInfos.isEmpty()) {
return
}
clearData()
// 将地图平移到 latLng 位置
val latLng = poiInfos[0].getLocation()
map.setMapStatus(mapStatus(latLng))
val itr: Iterator<*> = poiInfos.iterator()
val latLngs: MutableList<LatLng> = ArrayList()
var poiInfo: PoiInfo?
var i = 0
while (itr.hasNext()) {
poiInfo = itr.next() as PoiInfo?
if (null == poiInfo) {
continue
}
showPoiMarker(poiInfo, i)
latLngs.add(poiInfo.getLocation())
i++
}
setBounds(latLngs)
}
}
/**
* 最佳视野内显示所有点标记
*/
private fun setBounds(latLngs: List<LatLng>?) {
if (null == latLngs || latLngs.isEmpty()) {
return
}
val horizontalPadding = 80
val verticalPaddingBottom = 400
// 构造地理范围对象
val builder = LatLngBounds.Builder()
// 让该地理范围包含一组地理位置坐标
builder.include(latLngs)
_mapView.value?.apply {
// 设置显示在指定相对于MapView的padding中的地图地理范围
val mapStatusUpdate = MapStatusUpdateFactory.newLatLngBounds(
builder.build(),
horizontalPadding,
verticalPaddingBottom,
horizontalPadding,
verticalPaddingBottom
)
// 更新地图
map.setMapStatus(mapStatusUpdate)
// 设置地图上控件与地图边界的距离,包含比例尺、缩放控件、logo、指南针的位置
map.setViewPadding(
0,
0,
0,
verticalPaddingBottom
)
}
}
private fun clearData() {
Logger.i("清空地图数据")
_mapView.value?.apply {
map.clear()
mMarkerPoiInfo.clear()
mPreSelectMarker = null
}
}
/**
* 创建地图中心点marker
*/
private fun createCenterMarker(mBaiduMap: BaiduMap, mCenter: LatLng) {
Logger.i("标记坐标${mCenter}为地图中心点")
val projection: Projection = mBaiduMap.projection ?: return
val point = projection.toScreenLocation(mCenter)
val markerOptions = MarkerOptions()
.position(mCenter)
.icon(mBitmapDescWaterDrop)
.flat(false)
.fixedScreenPosition(point)
marker = markerOptions
mBaiduMap.addOverlay(markerOptions)
}
fun isLatlngEqual(latLng0: LatLng, latLng1: LatLng): Boolean {
return (latLng0.latitude == latLng1.latitude
&& latLng0.longitude == latLng1.longitude)
}
/**
* 逆地理编码请求
*
* @param latLng
*/
private fun reverseRequest(latLng: LatLng?) {
Logger.i("逆地理编码请求")
latLng?.let {
val reverseGeoCodeOption = ReverseGeoCodeOption().location(it)
.newVersion(1) // 建议请求新版数据
.radius(sDefaultRGCRadius)
mGeoCoder.reverseGeoCode(reverseGeoCodeOption)
}
}
private fun mapStatus(mCenter: LatLng): MapStatusUpdate {
return MapStatusUpdateFactory.newLatLngZoom(
mCenter,
16f
)
}
fun updateMapStatus(mapStatus: MapStatus?) {
mapStatus?.target?.let {
// 如果是点击poi item导致的地图状态更新,则不用做后面的逆地理请求,
if (_mStatusChangeByItemClick.value == true) {
if (!isLatlngEqual(
mCenter,
it
)
) {
mCenter = it
}
_mStatusChangeByItemClick.postValue(false)
return
}
if (!isLatlngEqual(mCenter, it)) {
mCenter = it
reverseRequest(mCenter)
}
}
}
private lateinit var mCenter: LatLng
private lateinit var marker: MarkerOptions
fun createMapView(context: Context): MapView {
val mapView = MapView(context).apply {
MyLocationListener.location.value?.let { _ ->
// 设置初始中心点为用户附近
// mCenter = LatLng(it.latitude, it.longitude)
mCenter = LatLng(39.963175, 116.400244)
map.setMapStatus(mapStatus(mCenter = mCenter))
map.setOnMapTouchListener {
ContextUtil.hideKeyBoard(context as Activity)
}
map.setOnMarkerClickListener {
scaffoldModel.update(message = "确定选中此地址", actionLabel = "确定") {
val poi = (_selectPoi.value ?: poiInfo.value?.get(0))
val address = poi?.address ?: poi?.name ?: "地址获取失败,请再尝试一次"
if (address.isNotEmpty()) {
activityAddress.onChange(address)
} else {
Logger.w("地址获取失败")
}
closeMap()
}
true
}
map.removeMarkerClickListener {
Logger.i("删除标记")
true
}
map.setOnMapLoadedCallback {
Logger.i("地图加载完成!")
// createCenterMarker(map, mCenter)
map.addOverlay(MarkerOptions().position(mCenter).icon(mBitmapDescWaterDrop))
reverseRequest(mCenter)
}
map.setOnMapStatusChangeListener(object :
BaiduMap.OnMapStatusChangeListener {
override fun onMapStatusChangeStart(p0: MapStatus?) {
Logger.i("状态开始改变:$p0")
}
override fun onMapStatusChangeStart(
p0: MapStatus?,
p1: Int
) {
Logger.i("状态改变:$p0,p1=$p1")
}
override fun onMapStatusChange(mapStatus: MapStatus?) {
Logger.i("状态改变:$mapStatus,_mStatusChangeByItemClick.value=${_mStatusChangeByItemClick.value}")
updateMapStatus(mapStatus = mapStatus)
map.clear()
map.addOverlay(MarkerOptions().position(mCenter).icon(mBitmapDescWaterDrop))
}
override fun onMapStatusChangeFinish(
mapStatus: MapStatus?
) {
Logger.i("状态改变完成:$mapStatus")
}
})
}
}
_mapView.value = mapView
return mapView
}
val address = object : StringForm(formDesc = "", textLength = 5) {
override val formPlaceholder = ""
override fun onChange(value: String) {
super.onChange(value)
Logger.i("Sug检索,城市:${city.formValue.value},地点关键词:${value}")
// _sugResult.value?.clear()
_sugResult.value = mutableListOf()
mSuggestionSearch.requestSuggestion(
SuggestionSearchOption()
.city(city.formValue.value)
.keyword(value)
.citylimit(true)
)
}
}
private val _showMap = MutableLiveData<Boolean>()
val showMap: LiveData<Boolean> = _showMap
private val _mStatusChangeByItemClick = MutableLiveData(false)
private val _poInfo = MutableLiveData<List<PoiInfo>>()
val poiInfo: LiveData<List<PoiInfo>> = _poInfo
private val _selectPoi = MutableLiveData<PoiInfo?>()
val selectPoi: LiveData<PoiInfo?> = _selectPoi
private val _sugResult = MutableLiveData<MutableList<SuggestionResult.SuggestionInfo>>()
val sugResult: LiveData<MutableList<SuggestionResult.SuggestionInfo>> = _sugResult
fun updateSelectPoi(poiInfo: PoiInfo?) {
_selectPoi.value = poiInfo
}
fun updateStatusChangeByItemClick(flag: Boolean) {
_mStatusChangeByItemClick.value = flag
}
fun openMap() {
_showMap.value = true
}
private fun closeMap() {
_showMap.value = false
clearData()
}
private fun destroy() {
mPoiSearch.destroy()
mSuggestionSearch.destroy()
mapView.value?.onDestroy()
mBitmapDescWaterDrop?.recycle()
}
/**
* TODO 提交申请
*
*/
fun apply(callback: (message: String) -> Unit) {
callback(NOT_IMPL_TIP)
}
}