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.

583 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 android.view.inputmethod.InputMethodManager
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.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 hideKeyBoard(activity: Activity?) {
Logger.i("隐藏软键盘")
activity?.apply {
val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
window.peekDecorView()?.let {
imm.hideSoftInputFromWindow(it.windowToken, 0)
}
}
}
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 {
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)
}
}