diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/popup_list_divider.xml b/app/src/main/res/drawable/popup_list_divider.xml new file mode 100644 index 0000000..10050ed --- /dev/null +++ b/app/src/main/res/drawable/popup_list_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/popup_list_divider.xml b/app/src/main/res/drawable/popup_list_divider.xml new file mode 100644 index 0000000..10050ed --- /dev/null +++ b/app/src/main/res/drawable/popup_list_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index da5a46f..a7fbbba 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:orientation="vertical" tools:context=".view.MainActivity"> - + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/popup_list_divider.xml b/app/src/main/res/drawable/popup_list_divider.xml new file mode 100644 index 0000000..10050ed --- /dev/null +++ b/app/src/main/res/drawable/popup_list_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index da5a46f..a7fbbba 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:orientation="vertical" tools:context=".view.MainActivity"> - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/popup_list_divider.xml b/app/src/main/res/drawable/popup_list_divider.xml new file mode 100644 index 0000000..10050ed --- /dev/null +++ b/app/src/main/res/drawable/popup_list_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index da5a46f..a7fbbba 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:orientation="vertical" tools:context=".view.MainActivity"> - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_map_popup.xml b/app/src/main/res/layout/item_map_popup.xml new file mode 100644 index 0000000..c62d55f --- /dev/null +++ b/app/src/main/res/layout/item_map_popup.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/popup_list_divider.xml b/app/src/main/res/drawable/popup_list_divider.xml new file mode 100644 index 0000000..10050ed --- /dev/null +++ b/app/src/main/res/drawable/popup_list_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index da5a46f..a7fbbba 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:orientation="vertical" tools:context=".view.MainActivity"> - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_map_popup.xml b/app/src/main/res/layout/item_map_popup.xml new file mode 100644 index 0000000..c62d55f --- /dev/null +++ b/app/src/main/res/layout/item_map_popup.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/marker_gaode.xml b/app/src/main/res/layout/marker_gaode.xml new file mode 100644 index 0000000..9bd62c2 --- /dev/null +++ b/app/src/main/res/layout/marker_gaode.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/popup_list_divider.xml b/app/src/main/res/drawable/popup_list_divider.xml new file mode 100644 index 0000000..10050ed --- /dev/null +++ b/app/src/main/res/drawable/popup_list_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index da5a46f..a7fbbba 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:orientation="vertical" tools:context=".view.MainActivity"> - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_map_popup.xml b/app/src/main/res/layout/item_map_popup.xml new file mode 100644 index 0000000..c62d55f --- /dev/null +++ b/app/src/main/res/layout/item_map_popup.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/marker_gaode.xml b/app/src/main/res/layout/marker_gaode.xml new file mode 100644 index 0000000..9bd62c2 --- /dev/null +++ b/app/src/main/res/layout/marker_gaode.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popu_map_info.xml b/app/src/main/res/layout/popu_map_info.xml new file mode 100644 index 0000000..7755ff3 --- /dev/null +++ b/app/src/main/res/layout/popu_map_info.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/popup_list_divider.xml b/app/src/main/res/drawable/popup_list_divider.xml new file mode 100644 index 0000000..10050ed --- /dev/null +++ b/app/src/main/res/drawable/popup_list_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index da5a46f..a7fbbba 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:orientation="vertical" tools:context=".view.MainActivity"> - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_map_popup.xml b/app/src/main/res/layout/item_map_popup.xml new file mode 100644 index 0000000..c62d55f --- /dev/null +++ b/app/src/main/res/layout/item_map_popup.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/marker_gaode.xml b/app/src/main/res/layout/marker_gaode.xml new file mode 100644 index 0000000..9bd62c2 --- /dev/null +++ b/app/src/main/res/layout/marker_gaode.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popu_map_info.xml b/app/src/main/res/layout/popu_map_info.xml new file mode 100644 index 0000000..7755ff3 --- /dev/null +++ b/app/src/main/res/layout/popu_map_info.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popup_option.xml b/app/src/main/res/layout/popup_option.xml new file mode 100644 index 0000000..149eb87 --- /dev/null +++ b/app/src/main/res/layout/popup_option.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ea56300..0416db4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,5 +49,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt index 291a985..c41c867 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt @@ -82,14 +82,40 @@ } //窨井类型转换 -fun String.toChinese(): String { +fun String.toTypeName(): String { return when (this) { - "0" -> "全部" - "1" -> "一级" - "2" -> "二级" - "3" -> "三级" + "1" -> "雨水井" + "2" -> "污水井" + "3" -> "燃气井" + "4" -> "热力井" + "5" -> "电力井" + "6" -> "交通井" + "7" -> "路灯井" + "8" -> "通信井" + "9" -> "监控井" else -> { - "未知" + "其他" + } + } +} + +fun String.toDeptName(): String { + return when (this) { + "0" -> "顶级" + "1148031048115494913" -> "二院" + "1178242695136149506" -> "电力公司" + "1178242900132757506" -> "滨海热电" + "24" -> "燃气集团总公司" + "30" -> "203所" + "1399230677135265794" -> "厦门测试" + "25" -> "第一分公司" + "26" -> "第二分公司" + "27" -> "第三分公司" + "28" -> "第四分公司" + "29" -> "高压管网分公司" + "1143796514486353922" -> "第五分公司" + else -> { + "其他" } } } diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt index 26f3085..4d0c818 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/fragment/HomePageFragment.kt @@ -1,24 +1,326 @@ package com.casic.app.smartwell.sanxi.fragment +import android.graphics.Point +import android.os.Bundle +import android.util.Log +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 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.app.smartwell.sanxi.R -import com.casic.app.smartwell.sanxi.base.BaseFragment -import kotlinx.android.synthetic.main.include_base_title.* +import com.casic.app.smartwell.sanxi.extensions.show +import com.casic.app.smartwell.sanxi.extensions.showRouteOnMap +import com.casic.app.smartwell.sanxi.extensions.toDeptName +import com.casic.app.smartwell.sanxi.extensions.toTypeName +import com.casic.app.smartwell.sanxi.model.WellListModel +import com.casic.app.smartwell.sanxi.utils.Constant +import com.casic.app.smartwell.sanxi.vm.WellViewModel +import com.casic.app.smartwell.sanxi.widgets.EasyPopupWindow +import com.casic.app.smartwell.sanxi.widgets.GaoDeClusterMarkerView +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.pengxh.app.multilib.utils.SizeUtil +import com.pengxh.app.multilib.widget.dialog.AlertControlDialog +import kotlinx.android.synthetic.main.fragment_home.view.* -class HomePageFragment : BaseFragment() { +class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener, + AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { - override fun initLayoutView(): Int = R.layout.fragment_home + private val kTag = "HomePageFragment" + private lateinit var homeView: View + private lateinit var aMap: AMap + private lateinit var wellViewModel: WellViewModel - override fun setupTopBarLayout() { - leftBackView.visibility = View.GONE - titleView.text = "首页" + /** + * 所有窨井列表信息集合 + * */ + private var wellModels: MutableList = ArrayList() + + /** + * 所有的marker + */ + private var allMarkerOptions: MutableList = ArrayList() + + /** + * 视野内的marker + */ + private var markerOptionsInView: MutableList = ArrayList() + + /** + * 自定义Marker弹出框 + * */ + private var infoWindow: View? = null + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + homeView = inflater.inflate(R.layout.fragment_home, container, false) + homeView.titleView.text = "首页" + val easyPopupWindow = EasyPopupWindow(requireContext()) + easyPopupWindow.setupPopupData(Constant.POPUP_IMAGES, Constant.POPUP_TITLES) + homeView.rightOptionView.setOnClickListener { + easyPopupWindow.setOnPopupWindowClickListener(object : + EasyPopupWindow.OnPopupWindowClickListener { + override fun onPopupClick(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.3f + bottomSheetBehavior.isHideable = false + bottomSheetBehavior.peekHeight = SizeUtil.dp2px(requireContext(), 30f) + } + + //初始化vm + wellViewModel = ViewModelProvider(this).get(WellViewModel::class.java) + + //获取所有窨井数据 + wellViewModel.obtainAllWell() + wellViewModel.allWellModel.observe(viewLifecycleOwner, { + if (it.code == 200) { + val latitudeList: MutableList = ArrayList() + val longitudeList: MutableList = ArrayList() + it.data?.forEach { well -> + val lat = well.latGaode.toString() + val lng = well.lngGaode.toString() + if (lat.isNotBlank() && lng.isNotBlank()) { + //返回true代表当前位置在大陆、港澳地区,反之不在 + val latitude = lat.toDouble() + val longitude = lng.toDouble() + if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) { + //缓存所有设备详情,点击Marker时候需要 + wellModels.add(well) + //分别缓存经、纬度 + latitudeList.add(latitude) + longitudeList.add(longitude) + //将所有设备信息转化缓存为Marker点 + allMarkerOptions.add( + MarkerOptions() + .position(LatLng(latitude, longitude)) + .title(well.wellTypeName) + .snippet(well.wellName) + ) + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度不在国内,异常经纬度 ===> [${lng},${lat}]") + } + } else { + Log.d(kTag, "${well.wellCode}闸井经纬度异常,异常经纬度 ===> [${lng},${lat}]") + } + } + //计算所有点的中心点位置 + val centerLatLng = LatLng(latitudeList.average(), longitudeList.average()) + Log.d( + kTag, + "所有闸井中心点位置 ===> [${latitudeList.average()}, ${longitudeList.average()}]" + ) + //移动到指定经纬度 + val cameraPosition = CameraPosition(centerLatLng, 13f, 0f, 0f) + val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition) + aMap.animateCamera(cameraUpdate, 3000, null) + } + }) + + //地图初始化 + initMap(savedInstanceState) + return homeView } - override fun initData() { + 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) + //信息窗点击事件 + aMap.addOnInfoWindowClickListener(this) + } + + override fun onMapLoaded() { + //地图加载成功之后显示聚合点数据 + initClustersMarkers() + } + + override fun onCameraChange(p0: CameraPosition?) { } - override fun initEvent() { + //获取视野内的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 = ArrayList() + markerOptionsInView.forEach { + if (clustersMarkers.size == 0) { + //添加一个新的自定义marker + clustersMarkers.add( + GaoDeClusterMarkerView(requireContext(), it, proj, Constant.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, Constant.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 wellTypeView = v.findViewById(R.id.wellTypeView) + val wellCodeView = v.findViewById(R.id.wellCodeView) + val ownerShipView = v.findViewById(R.id.ownerShipView) + val locationView = v.findViewById(R.id.locationView) + + //绑定数据 + val clickedLatLng = marker?.position!! + for (well in wellModels) { + if (clickedLatLng.latitude == well.latGaode!!.toDouble() + && clickedLatLng.longitude == well.lngGaode!!.toDouble() + ) { + wellTypeView.text = String.format("井类型: ${well.wellType.toTypeName()}") + wellCodeView.text = String.format("井编号: ${well.wellCode}") + ownerShipView.text = String.format("权属单位: ${well.deptid.toDeptName()}") + locationView.text = String.format("详细位置: ${well.position}") + return infoWindow + } + } + "无法查看聚合点,请放大比例后再查看".show() + 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() + return + } + Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap( + requireContext() + ) + } + + override fun onCancelClick() { + + } + }).build().show() + } + } + + /***以下是地图生命周期管理************************************************************************/ + override fun onResume() { + super.onResume() + homeView.mapView.onResume() + //每次页面切换都需要重新刷新不同状态的窨井数量 +// wellViewModel.countWellByState() +// workOrderViewModel.countWorkOrderByState() + } + + 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() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java new file mode 100644 index 0000000..4e916a9 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/WellListModel.java @@ -0,0 +1,271 @@ +package com.casic.app.smartwell.sanxi.model; + +import java.util.List; + +public class WellListModel { + + private int code; + private List data; + private String message; + private boolean success; + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public static class DataDTO { + private String area; + private String bfzt; + private String bfztName; + private String coordinateX; + private String coordinateY; + private String deep; + private String deptName; + private String deptid; + private String id; + private String latBaidu; + private String latGaode; + private String lngBaidu; + private String lngGaode; + private String notes; + private String photos; + private String position; + private String staff; + private String tel; + private String ts; + private String valid; + private String watchData; + private String wellCode; + private String wellName; + private String wellType; + private String wellTypeName; + + public String getArea() { + return area; + } + + public void setArea(String area) { + this.area = area; + } + + public String getBfzt() { + return bfzt; + } + + public void setBfzt(String bfzt) { + this.bfzt = bfzt; + } + + public String getBfztName() { + return bfztName; + } + + public void setBfztName(String bfztName) { + this.bfztName = bfztName; + } + + public String getCoordinateX() { + return coordinateX; + } + + public void setCoordinateX(String coordinateX) { + this.coordinateX = coordinateX; + } + + public String getCoordinateY() { + return coordinateY; + } + + public void setCoordinateY(String coordinateY) { + this.coordinateY = coordinateY; + } + + public String getDeep() { + return deep; + } + + public void setDeep(String deep) { + this.deep = deep; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptid() { + return deptid; + } + + public void setDeptid(String deptid) { + this.deptid = deptid; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getLatBaidu() { + return latBaidu; + } + + public void setLatBaidu(String latBaidu) { + this.latBaidu = latBaidu; + } + + public String getLatGaode() { + return latGaode; + } + + public void setLatGaode(String latGaode) { + this.latGaode = latGaode; + } + + public String getLngBaidu() { + return lngBaidu; + } + + public void setLngBaidu(String lngBaidu) { + this.lngBaidu = lngBaidu; + } + + public String getLngGaode() { + return lngGaode; + } + + public void setLngGaode(String lngGaode) { + this.lngGaode = lngGaode; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getPhotos() { + return photos; + } + + public void setPhotos(String photos) { + this.photos = photos; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getStaff() { + return staff; + } + + public void setStaff(String staff) { + this.staff = staff; + } + + public String getTel() { + return tel; + } + + public void setTel(String tel) { + this.tel = tel; + } + + public String getTs() { + return ts; + } + + public void setTs(String ts) { + this.ts = ts; + } + + public String getValid() { + return valid; + } + + public void setValid(String valid) { + this.valid = valid; + } + + public String getWatchData() { + return watchData; + } + + public void setWatchData(String watchData) { + this.watchData = watchData; + } + + public String getWellCode() { + return wellCode; + } + + public void setWellCode(String wellCode) { + this.wellCode = wellCode; + } + + public String getWellName() { + return wellName; + } + + public void setWellName(String wellName) { + this.wellName = wellName; + } + + public String getWellType() { + return wellType; + } + + public void setWellType(String wellType) { + this.wellType = wellType; + } + + public String getWellTypeName() { + return wellTypeName; + } + + public void setWellTypeName(String wellTypeName) { + this.wellTypeName = wellTypeName; + } + } +} diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt index 0fb598b..d57d378 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt @@ -1,6 +1,7 @@ package com.casic.app.smartwell.sanxi.utils import android.Manifest +import com.casic.app.smartwell.sanxi.R object Constant { @@ -19,7 +20,6 @@ const val USER_DEVICE_TYPE = "userDeviceType" const val ACCOUNT = "account" const val PASSWORD = "password" - const val ALARM_TYPE = "alarmType" const val APP_AUTHORITY = "com.casic.app.smartwell.sanxi.fileprovider" const val CANCEL_ALARM_ACTION = "cancelAlarm" @@ -29,11 +29,11 @@ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米 const val DISTANCE = 5 //两点间距离阈值,单位:米 + val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) + val POPUP_TITLES = arrayOf("标准地图", "卫星地图") + // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf) val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防") val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成") val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理") - - // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite) - val POPUP_TITLES = arrayOf("标准地图", "卫星地图") } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt index 2bb0294..3c6fea3 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitService.kt @@ -153,6 +153,12 @@ ): String /** + * 获取获取闸井列表-不分页,地图用 + */ + @GET("/overview/wellList") + suspend fun obtainAllWell(@Header("token") token: String): String + + /** * 根据布防状态统计闸井数量接口 */ @GET("/well/countByBfzt") @@ -192,12 +198,6 @@ ): String /** - * 获取获取闸井列表-不分页,地图用 - */ - @GET("/well/list") - suspend fun obtainAllWell(@Header("token") token: String): String - - /** * 获取井下监控设备列表 * * @param id 窨井ID diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt index 35af71d..f67ccb6 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/retrofit/RetrofitServiceManager.kt @@ -151,6 +151,13 @@ return api.obtainOrderStatus(AuthenticationHelper.token!!) } + /** + * 获取获取闸井列表-不分页 + */ + suspend fun obtainAllWell(): String { + return api.obtainAllWell(AuthenticationHelper.token!!) + } + // /** // * 根据布防状态统计闸井数量接口 // */ @@ -190,13 +197,6 @@ // } // // /** -// * 获取获取闸井列表-不分页 -// */ -// suspend fun obtainAllWell(): String { -// return api.obtainAllWell(AuthenticationHelper.token!!) -// } -// -// /** // * 获取井下监控设备列表 // */ // suspend fun obtainMonitorResult(id: String): String { diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt index 87dd8dc..6a80a92 100644 --- a/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/vm/WellViewModel.kt @@ -7,6 +7,7 @@ import com.casic.app.smartwell.sanxi.extensions.show import com.casic.app.smartwell.sanxi.extensions.toErrorMessage import com.casic.app.smartwell.sanxi.model.WellDetailModel +import com.casic.app.smartwell.sanxi.model.WellListModel import com.casic.app.smartwell.sanxi.utils.LoadState import com.casic.app.smartwell.sanxi.utils.retrofit.RetrofitServiceManager import com.google.gson.Gson @@ -16,6 +17,7 @@ private val gson = Gson() val detailModel = MutableLiveData() + val allWellModel = MutableLiveData() fun obtainWellDetail(id: String) = launch({ loadState.value = LoadState.Loading @@ -34,4 +36,18 @@ loadState.value = LoadState.Fail it.printStackTrace() }) + + fun obtainAllWell() = launch({ + val response = RetrofitServiceManager.obtainAllWell() + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + allWellModel.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + response.toErrorMessage().show() + } + }, { + it.printStackTrace() + }) } \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt new file mode 100644 index 0000000..4fd4702 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/EasyPopupWindow.kt @@ -0,0 +1,77 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.* +import com.casic.app.smartwell.sanxi.R +import com.pengxh.app.multilib.utils.SizeUtil + +/** + * @description: 顶部下拉菜单 + * @author: Pengxh + * @email: 290677893@qq.com + * @date: 2019/12/28 20:35 + */ +class EasyPopupWindow(private val context: Context) : PopupWindow(context) { + + private lateinit var clickListener: OnPopupWindowClickListener + + init { + this.width = (SizeUtil.getScreenWidth(context) * 0.3).toInt() + this.height = ViewGroup.LayoutParams.WRAP_CONTENT + this.isOutsideTouchable = true + this.isFocusable = true + this.animationStyle = R.style.PopupAnimation + this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_option, null, false) + } + + fun setupPopupData(imageArray: Array, titleArray: Array) { + val popupListView = contentView.findViewById(R.id.popupListView) + popupListView.adapter = object : BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int = imageArray.size + + override fun getItem(position: Int): Any = imageArray[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getView(position: Int, convertView: View?, viewGroup: ViewGroup): View { + val view: View + val holder: PopupWindowHolder + if (convertView == null) { + view = inflater.inflate(R.layout.item_map_popup, null) + holder = PopupWindowHolder() + holder.imageView = view.findViewById(R.id.imageView) + holder.titleView = view.findViewById(R.id.titleView) + view.tag = holder + } else { + view = convertView + holder = view.tag as PopupWindowHolder + } + holder.imageView.setBackgroundResource(imageArray[position]) + holder.titleView.text = titleArray[position] + return view + } + } + popupListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, i, _ -> + clickListener.onPopupClick(i) + dismiss() + } + } + + interface OnPopupWindowClickListener { + fun onPopupClick(position: Int) + } + + fun setOnPopupWindowClickListener(windowClickListener: OnPopupWindowClickListener) { + this.clickListener = windowClickListener + } + + inner class PopupWindowHolder { + lateinit var imageView: ImageView + lateinit var titleView: TextView + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt new file mode 100644 index 0000000..28ccff3 --- /dev/null +++ b/app/src/main/java/com/casic/app/smartwell/sanxi/widgets/GaoDeClusterMarkerView.kt @@ -0,0 +1,102 @@ +package com.casic.app.smartwell.sanxi.widgets + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Point +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import com.amap.api.maps.Projection +import com.amap.api.maps.model.BitmapDescriptorFactory +import com.amap.api.maps.model.LatLng +import com.amap.api.maps.model.LatLngBounds +import com.amap.api.maps.model.MarkerOptions +import com.casic.app.smartwell.sanxi.R +import com.casic.app.smartwell.sanxi.extensions.toBitmap +import java.util.* + +class GaoDeClusterMarkerView( + private val context: Context, firstMarkers: MarkerOptions, + projection: Projection, gridSize: Int +) { + //当前可观区域里的 聚合过之后的集合 + private val includeMarkers: ArrayList + + // 创建区域 + val bounds: LatLngBounds + var options: MarkerOptions = MarkerOptions() + + init { + val screenLocation = projection.toScreenLocation(firstMarkers.position) + //范围类 + val southwestPoint = Point(screenLocation.x - gridSize, screenLocation.y + gridSize) + //范围类 + val northeastPoint = Point(screenLocation.x + gridSize, screenLocation.y - gridSize) + bounds = LatLngBounds( + projection.fromScreenLocation(southwestPoint), + projection.fromScreenLocation(northeastPoint) + ) + //设置初始化marker属性 + options.anchor(0.5f, 1.3f) + .title(firstMarkers.title) + .position(firstMarkers.position) + .icon(firstMarkers.icon) + .snippet(firstMarkers.snippet) + .draggable(false) + includeMarkers = ArrayList() + includeMarkers.add(firstMarkers) + } + + /** + * 添加marker + */ + fun addMarker(markerOptions: MarkerOptions) { + includeMarkers.add(markerOptions) // 添加到列表中 + } + + /** + * 设置聚合点的中心位置以及图标 + */ + fun setPositionAndIcon() { + val size = includeMarkers.size + var lat = 0.0 + var lng = 0.0 + // 一个的时候 + if (size == 1) { //设置marker单个属性 + // 设置marker位置 + options.position( + LatLng( + includeMarkers[0].position.latitude, + includeMarkers[0].position.longitude + ) + ) + } else { // 聚合的时候 + //设置marker聚合属性 + for (op in includeMarkers) { + lat += op.position.latitude + lng += op.position.longitude + } + // 设置marker的位置为中心位置为聚集点的平均位置 + options.position(LatLng(lat / size, lng / size)) + } + options.icon(BitmapDescriptorFactory.fromBitmap(getBitmap(size))) + } + + /** + * marker视图 + */ + private fun getBitmap(num: Int): Bitmap? { + val view = LayoutInflater.from(context).inflate(R.layout.marker_gaode, null) + val wellCountView = view.findViewById(R.id.wellCountView) + return if (num > 1) { + wellCountView.visibility = View.VISIBLE + wellCountView.text = String.format("${num}个") + wellCountView.gravity = Gravity.CENTER + view.toBitmap() + } else { + wellCountView.visibility = View.GONE + BitmapDescriptorFactory.fromResource(R.mipmap.well_location).bitmap + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml new file mode 100644 index 0000000..15e6a4b --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_black_transparent_radius_10.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_main_color.xml b/app/src/main/res/drawable/bg_solid_layout_main_color.xml new file mode 100644 index 0000000..e329b7d --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_main_color.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_layout_radius_20.xml b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml new file mode 100644 index 0000000..0c2abfe --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_layout_radius_20.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bottom_short_line.xml b/app/src/main/res/drawable/bottom_short_line.xml new file mode 100644 index 0000000..10c9062 --- /dev/null +++ b/app/src/main/res/drawable/bottom_short_line.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_change_map.xml b/app/src/main/res/drawable/ic_change_map.xml new file mode 100644 index 0000000..c567efa --- /dev/null +++ b/app/src/main/res/drawable/ic_change_map.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_map.xml b/app/src/main/res/drawable/ic_menu_map.xml new file mode 100644 index 0000000..85ab880 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_map.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_satellite.xml b/app/src/main/res/drawable/ic_satellite.xml new file mode 100644 index 0000000..9116ac8 --- /dev/null +++ b/app/src/main/res/drawable/ic_satellite.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/popup_list_divider.xml b/app/src/main/res/drawable/popup_list_divider.xml new file mode 100644 index 0000000..10050ed --- /dev/null +++ b/app/src/main/res/drawable/popup_list_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index da5a46f..a7fbbba 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:orientation="vertical" tools:context=".view.MainActivity"> - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_map_popup.xml b/app/src/main/res/layout/item_map_popup.xml new file mode 100644 index 0000000..c62d55f --- /dev/null +++ b/app/src/main/res/layout/item_map_popup.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/marker_gaode.xml b/app/src/main/res/layout/marker_gaode.xml new file mode 100644 index 0000000..9bd62c2 --- /dev/null +++ b/app/src/main/res/layout/marker_gaode.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popu_map_info.xml b/app/src/main/res/layout/popu_map_info.xml new file mode 100644 index 0000000..7755ff3 --- /dev/null +++ b/app/src/main/res/layout/popu_map_info.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popup_option.xml b/app/src/main/res/layout/popup_option.xml new file mode 100644 index 0000000..149eb87 --- /dev/null +++ b/app/src/main/res/layout/popup_option.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b70cc4a..dd0459a 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -101,4 +101,20 @@ @dimen/dp_10 @drawable/ic_right + + + +