package com.casic.smarttube.fragment import android.graphics.Point import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.DividerItemDecoration import com.amap.api.maps.AMap import com.amap.api.maps.AMapOptions import com.amap.api.maps.CameraUpdateFactory import com.amap.api.maps.CoordinateConverter import com.amap.api.maps.model.* import com.casic.smarttube.R import com.casic.smarttube.adapter.GroupListAdapter import com.casic.smarttube.model.MapDeviceModel import com.casic.smarttube.model.ProjectGroupModel import com.casic.smarttube.utils.DialogHelper import com.casic.smarttube.utils.LocaleConstant import com.casic.smarttube.view.AddDeviceActivity import com.casic.smarttube.vm.DeviceViewModel import com.casic.smarttube.vm.ProjectGroupViewModel import com.casic.smarttube.widgets.GaoDeClusterMarkerView import com.google.android.material.bottomsheet.BottomSheetBehavior import com.pengxh.kt.lite.extensions.* import com.pengxh.kt.lite.vm.LoadState import com.pengxh.kt.lite.widget.EasyPopupWindow import com.pengxh.kt.lite.widget.dialog.AlertControlDialog import kotlinx.android.synthetic.main.fragment_home.view.* class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { private val kTag = "HomePageFragment" private lateinit var homeView: View private lateinit var aMap: AMap private lateinit var deviceViewModel: DeviceViewModel private lateinit var groupViewModel: ProjectGroupViewModel private lateinit var groupListAdapter: GroupListAdapter /** * 所有的marker */ private var allMarkerOptions: MutableList<MarkerOptions> = ArrayList() /** * 视野内的marker */ private var markerOptionsInView: MutableList<MarkerOptions> = ArrayList() /** * 自定义Marker弹出框 * */ private var infoWindow: View? = null /** * 所有设备列表信息集合 * */ private var deviceModels: MutableList<MapDeviceModel.DataModel> = ArrayList() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { homeView = inflater.inflate(R.layout.fragment_home, container, false) val easyPopupWindow = EasyPopupWindow(requireContext()) easyPopupWindow.setPopupMenuItem(LocaleConstant.POPUP_IMAGES, LocaleConstant.POPUP_TITLES) homeView.rightOptionView.setOnClickListener { easyPopupWindow.setOnPopupWindowClickListener(object : EasyPopupWindow.OnPopupWindowClickListener { override fun onPopupItemClicked(position: Int) { when (position) { 0 -> aMap.mapType = AMap.MAP_TYPE_NORMAL 1 -> aMap.mapType = AMap.MAP_TYPE_SATELLITE } } }) easyPopupWindow.setBackgroundDrawable(null) val x: Int = homeView.rightOptionView.width - easyPopupWindow.width easyPopupWindow.showAsDropDown(homeView.rightOptionView, x, 0) } //代码设置底部拉升距离 val bottomSheetBehavior = BottomSheetBehavior.from(homeView.bottomBehaviorLayout) homeView.coordinatorLayout.post { bottomSheetBehavior.isFitToContents = false bottomSheetBehavior.halfExpandedRatio = 0.33f bottomSheetBehavior.isHideable = false bottomSheetBehavior.peekHeight = 30f.dp2px(requireContext()) bottomSheetBehavior.setExpandedOffset( (requireContext().obtainScreenHeight() * 0.3).toInt() ) } //初始化vm deviceViewModel = ViewModelProvider(this).get(DeviceViewModel::class.java) groupViewModel = ViewModelProvider(this).get(ProjectGroupViewModel::class.java) //地图初始化 initMap(savedInstanceState) homeView.addDeviceButton.setOnClickListener { requireContext().navigatePageTo<AddDeviceActivity>() } return homeView } override fun onResume() { super.onResume() homeView.mapView.onResume() //获取所有设备数据 deviceViewModel.obtainMapDeviceList() //获取组 groupViewModel.obtainProGroupList() deviceViewModel.mapDeviceModel.observe(this, { if (it.code == 200) { val latitudeList: MutableList<Double> = ArrayList() val longitudeList: MutableList<Double> = ArrayList() it.data.forEach { device -> val lat = device.latGaode.toString() val lng = device.lngGaode.toString() if (lat.isNotBlank() && lng.isNotBlank()) { //返回true代表当前位置在大陆、港澳地区,反之不在 val latitude = lat.toDouble() val longitude = lng.toDouble() if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { //缓存所有设备详情,点击Marker时候需要 deviceModels.add(device) //分别缓存经、纬度 latitudeList.add(latitude) longitudeList.add(longitude) //将所有设备信息转化缓存为Marker点 allMarkerOptions.add( MarkerOptions() .position(LatLng(latitude, longitude)) .title(device.devcode) .snippet(device.modelName) ) } } } val centerLatLng = LatLng(latitudeList[0], longitudeList[0]) moveToPosition(centerLatLng) } }) groupViewModel.groupModel.observe(this, { if (it.code == 200) { bindRecyclerView(it.data) } }) } private fun initMap(savedInstanceState: Bundle?) { homeView.mapView.onCreate(savedInstanceState) aMap = homeView.mapView.map aMap.mapType = AMap.MAP_TYPE_NORMAL val uiSettings = aMap.uiSettings uiSettings.isCompassEnabled = true uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度 uiSettings.isRotateGesturesEnabled = false//不允许地图旋转 // 地图加载成功监听 aMap.addOnMapLoadedListener(this) // 地图缩放监听 aMap.addOnCameraChangeListener(this) // marker 点击事件监听 aMap.addOnMarkerClickListener(this) // 点击marker弹出自定义popup aMap.setInfoWindowAdapter(this) } private fun bindRecyclerView(dataBeans: MutableList<ProjectGroupModel.DataBean>) { groupListAdapter = GroupListAdapter(requireContext(), dataBeans) homeView.homeRecyclerView!!.addItemDecoration( DividerItemDecoration( requireContext(), DividerItemDecoration.VERTICAL ) ) homeView.homeRecyclerView!!.adapter = groupListAdapter groupListAdapter.setOnItemClickListener(object : GroupListAdapter.OnItemClickListener { override fun onClicked(position: Int) { // 根据groupId查询组下设备 groupViewModel.obtainDeviceListByGroup(dataBeans[position].groupId) } }) groupViewModel.groupDeviceModel.observe(this, { if (it.code == 200) { val latitudeList: MutableList<Double> = ArrayList() val longitudeList: MutableList<Double> = ArrayList() it.data.forEach { device -> val lat = device.latGaode.toString() val lng = device.lngGaode.toString() if (lat.isNotBlank() && lng.isNotBlank()) { //返回true代表当前位置在大陆、港澳地区,反之不在 val latitude = lat.toDouble() val longitude = lng.toDouble() if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { //分别缓存经、纬度 latitudeList.add(latitude) longitudeList.add(longitude) } } } //计算所有点的中心点位置 val centerLatLng = LatLng(latitudeList[0], longitudeList[0]) moveToPosition(centerLatLng) } }) groupViewModel.loadState.observe(this, { when (it) { LoadState.Loading -> DialogHelper.showLoadingDialog(requireActivity(), "数据加载中...") else -> DialogHelper.dismissLoadingDialog() } }) } //移动到指定经纬度 private fun moveToPosition(latLng: LatLng) { //移动到指定经纬度 val cameraPosition = CameraPosition(latLng, 13f, 0f, 0f) val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) aMap.animateCamera(cameraUpdate, 1500, null) } override fun onMapLoaded() { //地图加载成功之后显示聚合点数据 initClustersMarkers() } override fun onCameraChange(p0: CameraPosition?) { } //获取视野内的marker 根据聚合算法合成自定义的marker 显示视野内的marker override fun onCameraChangeFinish(p0: CameraPosition?) { //地图缩放之后显示聚合点数据 initClustersMarkers() } private fun initClustersMarkers() { val proj = aMap.projection val dm = resources.displayMetrics var screenLocation: Point markerOptionsInView.clear() // 获取在当前视野内的marker allMarkerOptions.forEach { screenLocation = proj.toScreenLocation(it.position) if (screenLocation.x >= 0 && screenLocation.y >= 0 && screenLocation.x <= dm.widthPixels && screenLocation.y <= dm.heightPixels) { //在当前可观区域内 markerOptionsInView.add(it) } } // 自定义的聚合类MarkerCluster val clustersMarkers: MutableList<GaoDeClusterMarkerView> = ArrayList() markerOptionsInView.forEach { if (clustersMarkers.size == 0) { //添加一个新的自定义marker clustersMarkers.add( GaoDeClusterMarkerView(requireContext(), it, proj, LocaleConstant.RADIUS_SIZE) ) } else { var isInRange = false //Kotlin foreach不能用break for (view in clustersMarkers) { //判断当前的marker是否在前面marker的聚合范围内 并且每个marker只会聚合一次。 if (view.bounds.contains(it.position)) { view.addMarker(it) isInRange = true break } } //如果没在任何范围内,自己单独形成一个自定义marker。在和后面的marker进行比较 if (!isInRange) { clustersMarkers.add( GaoDeClusterMarkerView( requireContext(), it, proj, LocaleConstant.RADIUS_SIZE ) )//相距多少才聚合 } } } // 设置聚合点的位置和icon clustersMarkers.forEach { it.setPositionAndIcon() } aMap.clear() // 重新添加 marker clustersMarkers.forEach { aMap.addMarker(it.options) } } override fun onMarkerClick(marker: Marker?): Boolean { //显示设备基本信息 marker?.showInfoWindow() return true } override fun getInfoWindow(marker: Marker?): View? { if (infoWindow == null) { infoWindow = LayoutInflater.from(requireContext()).inflate(R.layout.popu_map_info, null) } val v = infoWindow!! //反射得到popup里面的控件对象 val deviceCodeView = v.findViewById<TextView>(R.id.deviceCodeView) val deviceModelView = v.findViewById<TextView>(R.id.deviceModelView) val deviceValueView = v.findViewById<TextView>(R.id.deviceValueView) val updateTimeView = v.findViewById<TextView>(R.id.updateTimeView) val locationView = v.findViewById<TextView>(R.id.locationView) //绑定数据 val clickedLatLng = marker?.position!! for (device in deviceModels) { if (clickedLatLng.latitude == device.latGaode!!.toDouble() && clickedLatLng.longitude == device.lngGaode!!.toDouble() ) { deviceCodeView.text = String.format("设备编号: ${device.devcode}") deviceModelView.text = String.format("设备模型: ${device.modelName}") //获取设备最新浓度信息 deviceViewModel.obtainTubeLastData(device.devcode) deviceViewModel.lastDataModel.observe(requireActivity(), { if (it.code == 200) { deviceValueView.text = String.format("最新浓度: ${it.data.strength}") updateTimeView.text = String.format("更新时间: ${it.data.uptime}") } else { deviceValueView.text = String.format("最新浓度: 未知") updateTimeView.text = String.format("更新时间: 未知") } }) locationView.text = String.format("详细位置: ${device.position}") return infoWindow } } "无法查看聚合点,请放大比例后再查看".show(requireContext()) return null } /** * 此方法不能修改整个 InfoWindow 的背景和边框,无论自定义的样式是什么样,SDK 都会在最外层添加一个默认的边框 * */ override fun getInfoContents(p0: Marker?): View? = null override fun onInfoWindowClick(p0: Marker?) { if (p0 != null) { AlertControlDialog.Builder() .setContext(requireContext()) .setTitle("操作提示") .setMessage("确定要前往吗") .setNegativeButton("取消") .setPositiveButton("确定") .setOnDialogButtonClickListener(object : AlertControlDialog.OnDialogButtonClickListener { override fun onConfirmClick() { val latLng = p0.position val lat = latLng.latitude.toString() val lng = latLng.longitude.toString() if (lat.isBlank() || lng.isBlank()) { "窨井经纬度异常,无法开启导航".show(requireContext()) return } Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( requireContext() ) } override fun onCancelClick() { } }).build().show() } } /***以下是地图生命周期管理************************************************************************/ override fun onPause() { super.onPause() homeView.mapView.onPause() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) homeView.mapView.onSaveInstanceState(outState) } override fun onDestroy() { super.onDestroy() homeView.mapView.onDestroy() } }