Newer
Older
CasicSmartTube / app / src / main / java / com / casic / smarttube / fragment / HomePageFragment.kt
package com.casic.smarttube.fragment

import android.graphics.Point
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DividerItemDecoration
import com.amap.api.maps.AMap
import com.amap.api.maps.AMapOptions
import com.amap.api.maps.CameraUpdateFactory
import com.amap.api.maps.CoordinateConverter
import com.amap.api.maps.model.*
import com.casic.smarttube.R
import com.casic.smarttube.adapter.GroupListAdapter
import com.casic.smarttube.model.MapDeviceModel
import com.casic.smarttube.model.ProjectGroupModel
import com.casic.smarttube.utils.DialogHelper
import com.casic.smarttube.utils.LocaleConstant
import com.casic.smarttube.view.AddDeviceActivity
import com.casic.smarttube.vm.DeviceViewModel
import com.casic.smarttube.vm.ProjectGroupViewModel
import com.casic.smarttube.widgets.GaoDeClusterMarkerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.pengxh.kt.lite.extensions.*
import com.pengxh.kt.lite.vm.LoadState
import com.pengxh.kt.lite.widget.EasyPopupWindow
import com.pengxh.kt.lite.widget.dialog.AlertControlDialog
import kotlinx.android.synthetic.main.fragment_home.view.*

class HomePageFragment : Fragment(), AMap.OnMapLoadedListener, AMap.OnCameraChangeListener,
    AMap.OnMarkerClickListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener {

    private val kTag = "HomePageFragment"
    private lateinit var homeView: View
    private lateinit var aMap: AMap
    private lateinit var deviceViewModel: DeviceViewModel
    private lateinit var groupViewModel: ProjectGroupViewModel
    private lateinit var groupListAdapter: GroupListAdapter

    /**
     * 所有的marker
     */
    private var allMarkerOptions: MutableList<MarkerOptions> = ArrayList()

    /**
     * 视野内的marker
     */
    private var markerOptionsInView: MutableList<MarkerOptions> = ArrayList()

    /**
     * 自定义Marker弹出框
     * */
    private var infoWindow: View? = null

    /**
     * 所有设备列表信息集合
     * */
    private var deviceModels: MutableList<MapDeviceModel.DataModel> = ArrayList()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        homeView = inflater.inflate(R.layout.fragment_home, container, false)

        val easyPopupWindow = EasyPopupWindow(requireContext())
        easyPopupWindow.setPopupMenuItem(LocaleConstant.POPUP_IMAGES, LocaleConstant.POPUP_TITLES)
        homeView.rightOptionView.setOnClickListener {
            easyPopupWindow.setOnPopupWindowClickListener(object :
                EasyPopupWindow.OnPopupWindowClickListener {
                override fun onPopupItemClicked(position: Int) {
                    when (position) {
                        0 -> aMap.mapType = AMap.MAP_TYPE_NORMAL
                        1 -> aMap.mapType = AMap.MAP_TYPE_SATELLITE
                    }
                }
            })
            easyPopupWindow.setBackgroundDrawable(null)
            val x: Int = homeView.rightOptionView.width - easyPopupWindow.width
            easyPopupWindow.showAsDropDown(homeView.rightOptionView, x, 0)
        }

        //代码设置底部拉升距离
        val bottomSheetBehavior = BottomSheetBehavior.from(homeView.bottomBehaviorLayout)
        homeView.coordinatorLayout.post {
            bottomSheetBehavior.isFitToContents = false
            bottomSheetBehavior.halfExpandedRatio = 0.33f
            bottomSheetBehavior.isHideable = false
            bottomSheetBehavior.peekHeight = 30f.dp2px(requireContext())
            bottomSheetBehavior.setExpandedOffset(
                (requireContext().obtainScreenHeight() * 0.3).toInt()
            )
        }

        //初始化vm
        deviceViewModel = ViewModelProvider(this).get(DeviceViewModel::class.java)
        groupViewModel = ViewModelProvider(this).get(ProjectGroupViewModel::class.java)

        //地图初始化
        initMap(savedInstanceState)

        homeView.addDeviceButton.setOnClickListener {
            requireContext().navigatePageTo<AddDeviceActivity>()
        }
        return homeView
    }

    override fun onResume() {
        super.onResume()
        homeView.mapView.onResume()
        //获取所有设备数据
        deviceViewModel.obtainMapDeviceList()
        //获取组
        groupViewModel.obtainProGroupList()

        deviceViewModel.mapDeviceModel.observe(this, {
            if (it.code == 200) {
                val latitudeList: MutableList<Double> = ArrayList()
                val longitudeList: MutableList<Double> = ArrayList()
                it.data.forEach { device ->
                    val lat = device.latGaode.toString()
                    val lng = device.lngGaode.toString()
                    if (lat.isNotBlank() && lng.isNotBlank()) {
                        //返回true代表当前位置在大陆、港澳地区,反之不在
                        val latitude = lat.toDouble()
                        val longitude = lng.toDouble()
                        if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) {
                            //缓存所有设备详情,点击Marker时候需要
                            deviceModels.add(device)
                            //分别缓存经、纬度
                            latitudeList.add(latitude)
                            longitudeList.add(longitude)
                            //将所有设备信息转化缓存为Marker点
                            allMarkerOptions.add(
                                MarkerOptions()
                                    .position(LatLng(latitude, longitude))
                                    .title(device.devcode)
                                    .snippet(device.modelName)
                            )
                        }
                    }
                }
                val centerLatLng = LatLng(latitudeList[0], longitudeList[0])
                moveToPosition(centerLatLng)
            }
        })
        groupViewModel.groupModel.observe(this, {
            if (it.code == 200) {
                bindRecyclerView(it.data)
            }
        })
    }

    private fun initMap(savedInstanceState: Bundle?) {
        homeView.mapView.onCreate(savedInstanceState)
        aMap = homeView.mapView.map
        aMap.mapType = AMap.MAP_TYPE_NORMAL
        val uiSettings = aMap.uiSettings
        uiSettings.isCompassEnabled = true
        uiSettings.zoomPosition = AMapOptions.ZOOM_POSITION_RIGHT_CENTER
        uiSettings.isTiltGesturesEnabled = false//不许地图随手势倾斜角度
        uiSettings.isRotateGesturesEnabled = false//不允许地图旋转

        // 地图加载成功监听
        aMap.addOnMapLoadedListener(this)
        // 地图缩放监听
        aMap.addOnCameraChangeListener(this)
        // marker 点击事件监听
        aMap.addOnMarkerClickListener(this)
        // 点击marker弹出自定义popup
        aMap.setInfoWindowAdapter(this)
    }

    private fun bindRecyclerView(dataBeans: MutableList<ProjectGroupModel.DataBean>) {
        groupListAdapter = GroupListAdapter(requireContext(), dataBeans)
        homeView.homeRecyclerView!!.addItemDecoration(
            DividerItemDecoration(
                requireContext(), DividerItemDecoration.VERTICAL
            )
        )
        homeView.homeRecyclerView!!.adapter = groupListAdapter
        groupListAdapter.setOnItemClickListener(object : GroupListAdapter.OnItemClickListener {
            override fun onClicked(position: Int) {
                // 根据groupId查询组下设备
                groupViewModel.obtainDeviceListByGroup(dataBeans[position].groupId)
            }
        })
        groupViewModel.groupDeviceModel.observe(this, {
            if (it.code == 200) {
                val latitudeList: MutableList<Double> = ArrayList()
                val longitudeList: MutableList<Double> = ArrayList()
                it.data.forEach { device ->
                    val lat = device.latGaode.toString()
                    val lng = device.lngGaode.toString()
                    if (lat.isNotBlank() && lng.isNotBlank()) {
                        //返回true代表当前位置在大陆、港澳地区,反之不在
                        val latitude = lat.toDouble()
                        val longitude = lng.toDouble()
                        if (CoordinateConverter.isAMapDataAvailable(latitude, longitude)) {
                            //分别缓存经、纬度
                            latitudeList.add(latitude)
                            longitudeList.add(longitude)
                        }
                    }
                }
                //计算所有点的中心点位置
                val centerLatLng = LatLng(latitudeList[0], longitudeList[0])
                moveToPosition(centerLatLng)
            }
        })
        groupViewModel.loadState.observe(this, {
            when (it) {
                LoadState.Loading -> DialogHelper.showLoadingDialog(requireActivity(), "数据加载中...")
                else -> DialogHelper.dismissLoadingDialog()
            }
        })
    }

    //移动到指定经纬度
    private fun moveToPosition(latLng: LatLng) {
        //移动到指定经纬度
        val cameraPosition = CameraPosition(latLng, 13f, 0f, 0f)
        val cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition)
        aMap.animateCamera(cameraUpdate, 1500, null)
    }

    override fun onMapLoaded() {
        //地图加载成功之后显示聚合点数据
        initClustersMarkers()
    }

    override fun onCameraChange(p0: CameraPosition?) {

    }

    //获取视野内的marker 根据聚合算法合成自定义的marker 显示视野内的marker
    override fun onCameraChangeFinish(p0: CameraPosition?) {
        //地图缩放之后显示聚合点数据
        initClustersMarkers()
    }

    private fun initClustersMarkers() {
        val proj = aMap.projection
        val dm = resources.displayMetrics
        var screenLocation: Point
        markerOptionsInView.clear()
        // 获取在当前视野内的marker
        allMarkerOptions.forEach {
            screenLocation = proj.toScreenLocation(it.position)
            if (screenLocation.x >= 0 && screenLocation.y >= 0 && screenLocation.x <= dm.widthPixels && screenLocation.y <= dm.heightPixels) {
                //在当前可观区域内
                markerOptionsInView.add(it)
            }
        }
        // 自定义的聚合类MarkerCluster
        val clustersMarkers: MutableList<GaoDeClusterMarkerView> = ArrayList()
        markerOptionsInView.forEach {
            if (clustersMarkers.size == 0) {
                //添加一个新的自定义marker
                clustersMarkers.add(
                    GaoDeClusterMarkerView(requireContext(), it, proj, LocaleConstant.RADIUS_SIZE)
                )
            } else {
                var isInRange = false
                //Kotlin foreach不能用break
                for (view in clustersMarkers) {
                    //判断当前的marker是否在前面marker的聚合范围内 并且每个marker只会聚合一次。
                    if (view.bounds.contains(it.position)) {
                        view.addMarker(it)
                        isInRange = true
                        break
                    }
                }
                //如果没在任何范围内,自己单独形成一个自定义marker。在和后面的marker进行比较
                if (!isInRange) {
                    clustersMarkers.add(
                        GaoDeClusterMarkerView(
                            requireContext(),
                            it,
                            proj,
                            LocaleConstant.RADIUS_SIZE
                        )
                    )//相距多少才聚合
                }
            }
        }
        // 设置聚合点的位置和icon
        clustersMarkers.forEach {
            it.setPositionAndIcon()
        }
        aMap.clear()
        // 重新添加 marker
        clustersMarkers.forEach {
            aMap.addMarker(it.options)
        }
    }

    override fun onMarkerClick(marker: Marker?): Boolean {
        //显示设备基本信息
        marker?.showInfoWindow()
        return true
    }

    override fun getInfoWindow(marker: Marker?): View? {
        if (infoWindow == null) {
            infoWindow = LayoutInflater.from(requireContext()).inflate(R.layout.popu_map_info, null)
        }
        val v = infoWindow!!
        //反射得到popup里面的控件对象
        val deviceCodeView = v.findViewById<TextView>(R.id.deviceCodeView)
        val deviceModelView = v.findViewById<TextView>(R.id.deviceModelView)
        val deviceValueView = v.findViewById<TextView>(R.id.deviceValueView)
        val updateTimeView = v.findViewById<TextView>(R.id.updateTimeView)
        val locationView = v.findViewById<TextView>(R.id.locationView)

        //绑定数据
        val clickedLatLng = marker?.position!!
        for (device in deviceModels) {
            if (clickedLatLng.latitude == device.latGaode!!.toDouble()
                && clickedLatLng.longitude == device.lngGaode!!.toDouble()
            ) {
                deviceCodeView.text = String.format("设备编号: ${device.devcode}")
                deviceModelView.text = String.format("设备模型: ${device.modelName}")
                //获取设备最新浓度信息
                deviceViewModel.obtainTubeLastData(device.devcode)
                deviceViewModel.lastDataModel.observe(requireActivity(), {
                    if (it.code == 200) {
                        deviceValueView.text = String.format("最新浓度: ${it.data.strength}")
                        updateTimeView.text = String.format("更新时间: ${it.data.uptime}")
                    } else {
                        deviceValueView.text = String.format("最新浓度: 未知")
                        updateTimeView.text = String.format("更新时间: 未知")
                    }
                })
                locationView.text = String.format("详细位置: ${device.position}")
                return infoWindow
            }
        }
        "无法查看聚合点,请放大比例后再查看".show(requireContext())
        return null
    }

    /**
     * 此方法不能修改整个 InfoWindow 的背景和边框,无论自定义的样式是什么样,SDK 都会在最外层添加一个默认的边框
     * */
    override fun getInfoContents(p0: Marker?): View? = null

    override fun onInfoWindowClick(p0: Marker?) {
        if (p0 != null) {
            AlertControlDialog.Builder()
                .setContext(requireContext())
                .setTitle("操作提示")
                .setMessage("确定要前往吗")
                .setNegativeButton("取消")
                .setPositiveButton("确定")
                .setOnDialogButtonClickListener(object :
                    AlertControlDialog.OnDialogButtonClickListener {
                    override fun onConfirmClick() {
                        val latLng = p0.position
                        val lat = latLng.latitude.toString()
                        val lng = latLng.longitude.toString()
                        if (lat.isBlank() || lng.isBlank()) {
                            "窨井经纬度异常,无法开启导航".show(requireContext())
                            return
                        }
                        Poi(p0.snippet, LatLng(lat.toDouble(), lng.toDouble()), "").showRouteOnMap(
                            requireContext()
                        )
                    }

                    override fun onCancelClick() {

                    }
                }).build().show()
        }
    }

    /***以下是地图生命周期管理************************************************************************/

    override fun onPause() {
        super.onPause()
        homeView.mapView.onPause()
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        homeView.mapView.onSaveInstanceState(outState)
    }

    override fun onDestroy() {
        super.onDestroy()
        homeView.mapView.onDestroy()
    }
}