浏览代码

refactor(更新) :
- 修改开关状态页面样式
- 增加长按地图上点位显示点位信息弹窗
- 增加右侧列表隐藏功能
- 修改地图背景色
- 修改点位状态颜色
- 增加点位告警状态

周文健 2 月之前
父节点
当前提交
58db425d93

+ 18 - 4
app/src/main/java/com/grkj/iscs_mars/util/BitmapUtil.kt

@@ -91,15 +91,29 @@ object BitmapUtil {
      *
      * @param maxSide 默认 1024,即最长边 ≤ 1024 px
      */
-    fun decodePreview(file: File, maxSide: Int = 1024): Bitmap {
+    fun decodePreview(
+        file: File, maxSide: Int = 1024,
+        backgroundColor: Int? = null
+    ): Bitmap {   // 传 null 表示保持原样,传 Color.WHITE/BLACK 等增加底色
         val (w, h) = readImageSize(file)
-        // 采样倍数至少为 1;向下取整保证 decode 后尺寸 ≤ maxSide
         val sample = max(1, max(w, h) / maxSide)
         val opts = BitmapFactory.Options().apply {
             inSampleSize = sample
-            inPreferredConfig = Bitmap.Config.RGB_565   // 省内存
+            inPreferredConfig = Bitmap.Config.RGB_565
         }
-        return BitmapFactory.decodeFile(file.absolutePath, opts)
+        val src = BitmapFactory.decodeFile(file.absolutePath, opts)
+            ?: throw IllegalArgumentException("无法解码图片: ${file.absolutePath}")
+
+        // 如果不要底色,直接返回
+        if (backgroundColor == null) return src
+
+        // 创建一张不带透明通道的新图(RGB_565 就够了)
+        val out = Bitmap.createBitmap(src.width, src.height, Bitmap.Config.RGB_565)
+        val canvas = Canvas(out)
+        canvas.drawColor(backgroundColor)     // 先铺底色
+        canvas.drawBitmap(src, 0f, 0f, null)  // 再画原图
+        src.recycle() // 如果不再需要原始 bitmap,可以回收掉
+        return out
     }
 
     /*--------------------------------------------------------*/

+ 44 - 8
app/src/main/java/com/grkj/iscs_mars/view/activity/SwitchStatusActivity.kt

@@ -1,7 +1,9 @@
 package com.grkj.iscs_mars.view.activity
 
 import android.view.GestureDetector
+import android.view.Gravity
 import android.view.MotionEvent
+import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
@@ -18,8 +20,10 @@ import com.grkj.iscs_mars.databinding.ItemSwitchBinding
 import com.grkj.iscs_mars.modbus.ModBusController
 import com.grkj.iscs_mars.model.eventmsg.MsgEventConstants.MSG_EVENT_SWITCH_COLLECTION_UPDATE
 import com.grkj.iscs_mars.model.vo.map.MapInfoRespVO.IsMapPoint
+import com.grkj.iscs_mars.util.CommonUtils
 import com.grkj.iscs_mars.util.ToastUtils
 import com.grkj.iscs_mars.view.base.BaseMvpActivity
+import com.grkj.iscs_mars.view.dialog.SwitchInfoDialog
 import com.grkj.iscs_mars.view.iview.ISwitchStatusView
 import com.grkj.iscs_mars.view.presenter.SwitchStatusPresenter
 import com.grkj.iscs_mars.view.widget.CustomSwitchStationLayer
@@ -33,6 +37,7 @@ class SwitchStatusActivity :
     BaseMvpActivity<ISwitchStatusView, SwitchStatusPresenter, ActivitySwitchStatusBinding>() {
     private var stationLayer: CustomSwitchStationLayer? = null
     private lateinit var gestureDetector: GestureDetector
+    private lateinit var switchInfoDialog: SwitchInfoDialog
 
     override fun initPresenter(): SwitchStatusPresenter {
         return SwitchStatusPresenter()
@@ -42,6 +47,8 @@ class SwitchStatusActivity :
         get() = ActivitySwitchStatusBinding.inflate(layoutInflater)
 
     override fun initView() {
+        switchInfoDialog = SwitchInfoDialog(this)
+        switchInfoDialog.popupGravity = Gravity.TOP
         mBinding?.cbBack?.setDebouncedClickListener {
             finish()
         }
@@ -51,6 +58,16 @@ class SwitchStatusActivity :
                 return super.onDoubleTap(e)
             }
         })
+        mBinding?.cbShow?.setDebouncedClickListener {
+            if (mBinding?.pointList?.isVisible == true) {
+                mBinding?.pointList?.isVisible = false
+                mBinding?.cbShow?.rotation = 0f
+            } else {
+                mBinding?.pointList?.isVisible = true
+                mBinding?.cbShow?.rotation = 180f
+            }
+        }
+        mBinding?.mapview?.setBackgroundColorInt(getColor(R.color.color_map_base))
         mBinding?.rvList?.linear()?.divider {
             orientation = DividerOrientation.HORIZONTAL
             setColorRes(R.color.common_bg_black_30)
@@ -66,25 +83,36 @@ class SwitchStatusActivity :
     private fun BindingAdapter.BindingViewHolder.onRVListBinding() {
         val itemBinding = getBinding<ItemSwitchBinding>()
         val item = getModel<IsMapPoint>()
-        itemBinding.index.text = "${item.pointSerialNumber}"
-        itemBinding.pointName.text = item.entityName
         val switchData = ModBusController.getSwitchData()
+        itemBinding.switchName.text = item.entityName
+        itemBinding.switchId.text = context.getString(R.string.switch_id, item.pointSerialNumber)
         val switchStatus = switchData
             .find { it.idx == item.pointSerialNumber?.toInt() }?.enabled
             ?: (item.switchStatus == "1")
-        itemBinding.status.text = if (switchStatus) {
-            getString(R.string.switch_open)
-        } else {
-            getString(R.string.switch_close)
+        when (switchStatus) {
+            false -> {
+                itemBinding.switchStatus.setBackgroundResource(R.drawable.bg_switch_off)
+                itemBinding.switchStatus.text = CommonUtils.getStr(R.string.switch_close)
+            }
+
+            true -> {
+                itemBinding.switchStatus.setBackgroundResource(R.drawable.bg_switch_on)
+                itemBinding.switchStatus.text = CommonUtils.getStr(R.string.switch_open)
+            }
+
+            else -> {
+                itemBinding.switchStatus.setBackgroundResource(R.drawable.bg_switch_alarm)
+                itemBinding.switchStatus.text = CommonUtils.getStr(R.string.switch_alarm)
+            }
         }
-        itemBinding.position.setDebouncedClickListener {
+        itemBinding.root.setDebouncedClickListener {
             stationLayer?.selectPoint(item.pointSerialNumber.toString())
         }
     }
 
     override fun onResume() {
         super.onResume()
-        BusinessManager.mEventBus.observe(this)  {
+        BusinessManager.mEventBus.observe(this) {
             when (it.code) {
                 MSG_EVENT_SWITCH_COLLECTION_UPDATE -> {
                     mBinding?.rvList?.adapter?.notifyDataSetChanged()
@@ -133,6 +161,14 @@ class SwitchStatusActivity :
                     stationLayer = CustomSwitchStationLayer(
                         mBinding?.mapview, presenter?.mStationList ?: mutableListOf()
                     )
+                    stationLayer?.onLongPressListener = { point, screenX, screenY, _, _ ->
+                        switchInfoDialog.setSwitchInfo(
+                            point.entityName,
+                            "${point.pointSerialNumber}",
+                            point.status
+                        )
+                        switchInfoDialog.showPopupWindow(screenX.toInt(), screenY.toInt())
+                    }
                     mBinding?.mapview?.addLayer(stationLayer)
                     stationLayer?.setRatio(presenter?.mapRatio ?: 1f)
                     stationLayer?.stopAnimation()

+ 48 - 0
app/src/main/java/com/grkj/iscs_mars/view/dialog/SwitchInfoDialog.kt

@@ -0,0 +1,48 @@
+package com.grkj.iscs_mars.view.dialog
+
+import android.content.Context
+import android.view.View
+import com.grkj.iscs_mars.R
+import com.grkj.iscs_mars.databinding.DialogSwitchInfoBinding
+import com.grkj.iscs_mars.util.CommonUtils
+import razerdp.basepopup.BasePopupWindow
+
+/**
+ * 开关信息弹窗
+ */
+class SwitchInfoDialog(context: Context) : BasePopupWindow(context) {
+    private lateinit var binding: DialogSwitchInfoBinding
+
+    init {
+        setContentView(R.layout.dialog_switch_info)
+    }
+
+    override fun onViewCreated(contentView: View) {
+        super.onViewCreated(contentView)
+        setBackground(null)
+        setFitSize(true)
+        setOverlayMask(false)
+        binding = DialogSwitchInfoBinding.bind(contentView)
+    }
+
+    fun setSwitchInfo(switchName: String, switchId: String, switchStatus: Int) {
+        binding.switchName.text = switchName
+        binding.switchId.text = context.getString(R.string.switch_id,switchId)
+        when (switchStatus) {
+            0 -> {
+                binding.switchStatus.setBackgroundResource(R.drawable.bg_switch_off)
+                binding.switchStatus.text = CommonUtils.getStr(R.string.switch_close)
+            }
+
+            1 -> {
+                binding.switchStatus.setBackgroundResource(R.drawable.bg_switch_on)
+                binding.switchStatus.text = CommonUtils.getStr(R.string.switch_open)
+            }
+
+            2 -> {
+                binding.switchStatus.setBackgroundResource(R.drawable.bg_switch_alarm)
+                binding.switchStatus.text = CommonUtils.getStr(R.string.switch_alarm)
+            }
+        }
+    }
+}

+ 2 - 1
app/src/main/java/com/grkj/iscs_mars/view/presenter/SwitchStatusPresenter.kt

@@ -6,6 +6,7 @@ import android.graphics.PointF
 import com.google.gson.Gson
 import com.google.gson.reflect.TypeToken
 import com.grkj.iscs_mars.MyApplication
+import com.grkj.iscs_mars.R
 import com.grkj.iscs_mars.extentions.serialNo
 import com.grkj.iscs_mars.model.SimData
 import com.grkj.iscs_mars.model.vo.BaseVO
@@ -74,7 +75,7 @@ class SwitchStatusPresenter : BasePresenter<ISwitchStatusView>() {
         }
 
         /* --- 2. 快速首屏预览 --- */
-        BitmapUtil.decodePreview(imgFile).also(onPreview)     // 预览 Bitmap 交给 UI
+        BitmapUtil.decodePreview(imgFile, backgroundColor = context.getColor(R.color.color_map_base)).also(onPreview)     // 预览 Bitmap 交给 UI
 
         /* --- 3. 拿原图尺寸,算缩放 --- */
         val (actualW, actualH) = BitmapUtil.readImageSize(imgFile)

+ 369 - 118
app/src/main/java/com/grkj/iscs_mars/view/widget/CustomSwitchStationLayer.kt

@@ -7,17 +7,17 @@ import android.graphics.Matrix
 import android.graphics.Paint
 import android.graphics.PointF
 import android.os.SystemClock
-import android.util.Pair
 import android.view.MotionEvent
 import androidx.core.content.ContextCompat
 import com.grkj.iscs_mars.MyApplication
 import com.grkj.iscs_mars.R
+import com.grkj.iscs_mars.modbus.DockBean
 import com.grkj.iscs_mars.modbus.ModBusController
-import com.grkj.iscs_mars.util.BitmapUtil
 import com.onlylemi.mapview.library.MapView
 import com.onlylemi.mapview.library.layer.MapBaseLayer
+import com.sik.sikcore.SIKCore
 import com.sik.sikcore.thread.ThreadUtils
-import kotlin.math.cos
+import kotlin.math.abs
 import kotlin.math.sin
 
 /**
@@ -26,34 +26,98 @@ import kotlin.math.sin
 class CustomSwitchStationLayer @JvmOverloads constructor(
     mapView: MapView?, private var stationList: MutableList<IsolationPoint> = mutableListOf()
 ) : MapBaseLayer(mapView) {
+
     @Volatile
     var inDraw: Boolean = false
         private set
 
-    // 呼吸灯周期(毫秒)
-    private val breathePeriod = 1200f
-    private val FRAME_INTERVAL = 32L   // 约 30fps
+    // ---- 动画参数 ----
+    private val breathePeriod = 1200f       // ALARM 脉冲周期(ms)
+    private val FRAME_INTERVAL = 32L        // ~30fps
+    private var pulsePhase: Float = 0f      // 0..1 脉冲相位
+
+    // —— 动画时长配置 ——
+    private val longPressCenterDurationMs = 280L
+    private val clickZoomDurationMs = 220L
+    private val clickCenterDurationMs = 220L
+
+    // 点击后希望的目标缩放(你想要多大就设多少)
+    private val desiredSelectScale = 2.0f
+
+    // 旧的 alpha 给 ON 态呼吸用的;现在只给 ALARM 脉冲使用
     private var alpha = 255
-    private val refreshRunnable = Runnable {
-        // 2. uptimeMillis 保证单调递增
-        val now = SystemClock.uptimeMillis()
-        // 3. 先在 Long 上做模,再转 Float 计算 phase
-        val phase = ((now % breathePeriod).toFloat()) / breathePeriod
-        val normalized = ((sin(phase * 2 * Math.PI) + 1) / 2).toFloat()
-        alpha = (normalized * (255 - 50) + 50).toInt()
-        mapView?.refresh()
-        mapView?.post {
-            startAnimation()
+
+    private val refreshRunnable: Runnable = object : Runnable {
+        override fun run() {
+            val now = SystemClock.uptimeMillis()
+            val phase = ((now % breathePeriod).toFloat()) / breathePeriod
+            pulsePhase = phase
+            alpha = (((sin(phase * 2 * Math.PI) + 1) / 2f) * (255 - 50) + 50).toInt()
+
+            val points = synchronized(stationList) { stationList.toList() }
+            val switches = runCatching { ModBusController.getSwitchData() }.getOrNull()
+
+            if (hasAnyAlarm(points, switches)) {
+                mapView?.refresh()
+                mapView?.postDelayed(this, FRAME_INTERVAL) // 🔑 用 this 引用自己
+            } else {
+                mapView?.refresh()
+            }
         }
     }
+
     private lateinit var paint: Paint
     private var currentZoom = 0f
     private var currentDegree = 0f
     private var ratio: Float = 1f
-    private var switchSize: Float = 1f
+
+    // 尺寸:注意你把 switchSize 当作「半径」来使用
+    private var switchSize: Float = 2f
     private var textSize: Float = 1f
     private var lastKnownZoom: Float = 1f
 
+    // 长按探测
+    private val longPressTimeoutMs: Long =
+        android.view.ViewConfiguration.getLongPressTimeout().toLong()
+    // 建议更宽容一些(x1.5):
+    private val touchSlop: Float =
+        android.view.ViewConfiguration.get(mapView?.context ?: SIKCore.getApplication()).scaledTouchSlop * 1.5f
+    private val touchSlopSq: Float = touchSlop * touchSlop
+
+    private var pendingLongPress = false
+    private var longPressTriggered = false
+    private var downScreenX = 0f
+    private var downScreenY = 0f
+    private var lastScreenX = 0f
+    private var lastScreenY = 0f
+    private var currentPressed: IsolationPoint? = null
+
+    private val longPressRunnable = Runnable {
+        if (pendingLongPress && currentPressed != null) {
+            longPressTriggered = true
+            pendingLongPress = false  // ✅ 关键:触发后不再允许被 MOVE 取消
+
+            val p = currentPressed!!
+            val centerMapX = p.pos.x + switchSize / 2f
+            val centerMapY = p.pos.y + switchSize / 2f
+
+            mapView?.animateCenterOnPoint(centerMapX, centerMapY, longPressCenterDurationMs, lastKnownZoom)
+            mapView?.postDelayed({
+                val screen = mapToScreen(centerMapX, centerMapY)
+                onLongPressListener?.invoke(p, screen.x, screen.y, centerMapX, centerMapY)
+            }, longPressCenterDurationMs)
+        }
+    }
+
+
+    /** 长按回调:点、屏幕坐标(已居中)、图内坐标(中心点) */
+    var onLongPressListener: ((
+        point: IsolationPoint,
+        screenX: Float, screenY: Float,
+        mapX: Float, mapY: Float
+    ) -> Unit)? = null
+
+
     init {
         initLayer()
     }
@@ -72,69 +136,170 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
             stationList.clear()
             stationList.addAll(points)
         }
-        mapView.postInvalidate()   // 触发重绘
+        mapView.postInvalidate()
     }
 
     /**
      * 若给定点位在屏幕可视区域内,则执行一次重绘;否则忽略。
-     * 可在异步加载小图标完成后调用,避免整图层频繁刷新。
      */
     fun refreshIfVisible(point: PointF, margin: Float = 0f) {
-        // 如果当前仍在 draw() 里,直接返回,等那一帧完成即可
         if (inDraw) return
-
         val w = mapView.width
         val h = mapView.height
         if (w == 0 || h == 0) return
 
-        // 将点位的原图坐标转换为屏幕坐标
         val pts = floatArrayOf(point.x, point.y)
-        // 假设 MapView 暴露 currentMatrix;如项目中名称不同请自行调整
         mapView.currentMatrix.mapPoints(pts)
-
         val x = pts[0]
         val y = pts[1]
-        if (x + margin >= 0 && x - margin <= w &&
-            y + margin >= 0 && y - margin <= h
-        ) {
+        if (x + margin >= 0 && x - margin <= w && y + margin >= 0 && y - margin <= h) {
             mapView.postInvalidate()
         }
     }
 
+    /**
+     * 全局比例设置(叠乘版)
+     * 如果不想叠乘(反复调用越来越大/小),改为基准写法:
+     *   switchSize = setValue(BASE_SWITCH_SIZE * ratio)
+     *   textSize = switchSize
+     */
     fun setRatio(ratio: Float) {
         this.ratio = ratio
-        switchSize = setValue(4 * ratio)
-        textSize = switchSize
+        switchSize = setValue(4 * ratio * switchSize)
+        textSize = switchSize * 0.8f
     }
 
     fun startAnimation() {
-        // 先干掉前一次没执行的
         mapView.removeCallbacks(refreshRunnable)
-        // 延后 16ms 再刷新,自动合并一堆连续的调用
         mapView.postDelayed(refreshRunnable, FRAME_INTERVAL)
     }
 
     fun stopAnimation() {
-        // 先干掉前一次没执行的
         mapView.removeCallbacks(refreshRunnable)
     }
 
-    override fun onTouch(event: MotionEvent) {
-        if (event.action != MotionEvent.ACTION_UP) return
-
-        // 屏幕坐标 → 图内坐标(和 draw() 的 concat 对应)
+    // —— 坐标互转工具 ——
+    private fun screenToMap(x: Float, y: Float): PointF? {
         val inv = Matrix()
         val m = Matrix(mapView.currentMatrix)
-        if (!m.invert(inv)) return
-        val pt = floatArrayOf(event.x, event.y)
+        if (!m.invert(inv)) return null
+        val pt = floatArrayOf(x, y)
         inv.mapPoints(pt)
-        val mapX = pt[0];
-        val mapY = pt[1]
+        return PointF(pt[0], pt[1])
+    }
+
+    private fun mapToScreen(mx: Float, my: Float): PointF {
+        val m = Matrix(mapView.currentMatrix)
+        val pt = floatArrayOf(mx, my)
+        m.mapPoints(pt)
+        return PointF(pt[0], pt[1])
+    }
+
+    /** 取“当前屏幕中心”所对应的图内坐标,用来实现只缩放不平移 */
+    private fun currentMapCenter(): PointF? {
+        val w = mapView.width
+        val h = mapView.height
+        if (w == 0 || h == 0) return null
+        return screenToMap(w * 0.5f, h * 0.5f)
+    }
+
+
+    override fun onTouch(event: MotionEvent) {
+        when (event.actionMasked) {
+            MotionEvent.ACTION_DOWN -> {
+                downScreenX = event.x
+                downScreenY = event.y
+                lastScreenX = event.x
+                lastScreenY = event.y
+
+                longPressTriggered = false
+                pendingLongPress = false
+                currentPressed = null
+
+                val mapPt = screenToMap(event.x, event.y)
+                if (mapPt != null) {
+                    val hit = hitTest(mapPt.x, mapPt.y)
+                    if (hit != null) {
+                        currentPressed = hit
+                        pendingLongPress = true
+                        mapView.parent?.requestDisallowInterceptTouchEvent(true) // ✅ 防父布局拦截
+                        mapView.removeCallbacks(longPressRunnable)
+                        mapView.postDelayed(longPressRunnable, longPressTimeoutMs)
+                    }
+
+                }
+            }
+
+            MotionEvent.ACTION_MOVE -> {
+                lastScreenX = event.x
+                lastScreenY = event.y
+                if (pendingLongPress && !longPressTriggered) {
+                    val dx = event.x - downScreenX
+                    val dy = event.y - downScreenY
+                    if (dx*dx + dy*dy > touchSlopSq) {
+                        pendingLongPress = false
+                        mapView.removeCallbacks(longPressRunnable)
+                    }
+                }
+            }
+
+            MotionEvent.ACTION_UP -> {
+                // 取消长按
+                mapView.removeCallbacks(longPressRunnable)
+
+                // 如果刚才已触发长按,吞掉点击
+                if (longPressTriggered) {
+                    pendingLongPress = false
+                    longPressTriggered = false
+                    currentPressed = null
+                    return
+                }
 
-        // 命中半径(用和绘制一致的图内半径,稍放宽)
+                // —— 点击:先放大,再居中,再标记(保证最终居中)——
+                val mapPt = screenToMap(event.x, event.y) ?: return
+                val hit = hitTest(mapPt.x, mapPt.y) ?: return
+
+                val targetScale = desiredSelectScale
+                val targetMapX = hit.pos.x + switchSize / 2f
+                val targetMapY = hit.pos.y + switchSize / 2f
+
+                // ① 只缩放:以当前屏幕中心为 pivot,把 scale 动到 targetScale
+                val curCenter = currentMapCenter()
+                if (curCenter != null) {
+                    mapView?.animateCenterOnPoint(curCenter.x, curCenter.y, clickZoomDurationMs, targetScale)
+                } else {
+                    // 万一拿不到中心,就直接在下一步里统一做
+                }
+
+                // ② 再居中到目标点(保持 targetScale),确保最后居中
+                mapView?.postDelayed({
+                    mapView?.animateCenterOnPoint(targetMapX, targetMapY, clickCenterDurationMs, targetScale)
+                }, clickZoomDurationMs)
+
+                // ③ 动画完成后再 selectPoint(此时位置稳定)
+                mapView?.postDelayed({
+                    hit.pointSerialNumber?.let { serial ->
+                        // 这里 selectPoint 内部再调用 animate 的 scale/center 就会乱,所以用“保持现状”:
+                        // 直接标记 + 刷新,不再让 selectPoint 改缩放/中心(你可以加个参数控制)
+                        stationList.replaceAll { it.copy(isSelected = (it.pointSerialNumber == serial)) }
+                        mapView?.postInvalidate()
+                    }
+                }, clickZoomDurationMs + clickCenterDurationMs)
+            }
+
+            MotionEvent.ACTION_CANCEL -> {
+                pendingLongPress = false
+                longPressTriggered = false
+                currentPressed = null
+                mapView.removeCallbacks(longPressRunnable)
+            }
+        }
+    }
+
+
+    private fun hitTest(mapX: Float, mapY: Float): IsolationPoint? {
         val hitR = switchSize * 1.2f
         val hitR2 = hitR * hitR
-
         val snapshot = synchronized(stationList) { stationList.toList() }
         var hit: IsolationPoint? = null
         var bestD2 = Float.MAX_VALUE
@@ -147,102 +312,161 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
                 hit = p
             }
         }
-
-        // 命中了才操作:用 pointSerialNumber 走你现成的 selectPoint;scale 用当前缩放
-        hit?.pointSerialNumber?.let { serial ->
-            selectPoint(
-                serial = serial,
-                animated = true,
-                durationMs = 300L,
-                scale = lastKnownZoom   // 不改缩放,只居中
-            )
-        }
+        return hit
     }
 
+
+
     override fun draw(
         canvas: Canvas, currentMatrix: Matrix, currentZoom: Float, currentRotateDegrees: Float
     ) {
-        if (!isVisible) return  // ← 只有这一个早退允许在 save 之前
-        if (inDraw) {
-            return
-        }
+        if (!isVisible) return
+        if (inDraw) return
         inDraw = true
+
         this.currentZoom = currentZoom
-        lastKnownZoom = currentZoom   // ← 记录下来
+        lastKnownZoom = currentZoom
         currentDegree = 360 - currentRotateDegrees
+
         try {
             canvas.save()
             try {
-// 把 mapView 本身的缩放/平移/旋转一次性 concat 到 Canvas
+                // 合并地图矩阵
                 canvas.concat(currentMatrix)
+
                 val switchData = ModBusController.getSwitchData()
                 val tempPointList = synchronized(stationList) { stationList.toList() }
+
+                // 如果存在 ALARM,则确保动画驱动起来
+                if (hasAnyAlarm(tempPointList, switchData)) {
+                    startAnimation()
+                }
+
                 tempPointList.forEach { point ->
-                    val switchStatus = switchData
-                        .find { it.idx == point.pointSerialNumber?.toInt() }?.enabled
-                    // point.pos.x/y 已经是「图内像素坐标」
+                    val bean = switchData.find { it.idx == point.pointSerialNumber?.toInt() }
+                    val status = parseStatus(bean)
+
                     val x = point.pos.x
                     val y = point.pos.y
-                    // 先画背景(它会被 currentMatrix 自动缩放)
-                    paint.alpha = 255
-                    if (switchStatus != null) {
-                        if (point.isSelected) {
-                            paint.color = Color.RED
-                            paint.strokeWidth = 2f
-                            paint.style = Paint.Style.STROKE
-                            canvas.drawRect(
-                                x - switchSize / 2 - paint.strokeWidth,
-                                y - switchSize / 2 - paint.strokeWidth,
-                                x + switchSize * 1.5f + paint.strokeWidth,
-                                y + switchSize * 1.5f + paint.strokeWidth,
-                                paint
-                            )
-                        }
-                        paint.style = Paint.Style.FILL_AND_STROKE
-                        // 再画 icon
-                        if (switchStatus) {
+
+                    // ---- 选中态:蓝色外环(想要回红框,把这段改回去即可)----
+                    if (point.isSelected) {
+                        paint.style = Paint.Style.STROKE
+                        paint.strokeWidth = 4f
+                        paint.color = Color.argb(180, 66, 133, 244) // #4285F4 带透明
+                        canvas.drawCircle(
+                            x + switchSize / 2,
+                            y + switchSize / 2,
+                            switchSize * 1.5f,
+                            paint
+                        )
+                    }
+
+                    // ---- 本体圆:按状态绘制 ----
+                    paint.style = Paint.Style.FILL_AND_STROKE
+                    when (status) {
+                        SwitchStatus.ON -> {
+                            stationList.firstOrNull { it.entityId == point.entityId }?.status = 1
                             paint.color = ContextCompat.getColor(
-                                MyApplication.instance?.applicationContext!!,
+                                MyApplication.instance!!.applicationContext,
                                 R.color.common_switch_enable
                             )
-                            paint.alpha = alpha
+                            paint.alpha = 255
                             canvas.drawCircle(
                                 x + switchSize / 2,
                                 y + switchSize / 2,
-                                switchSize, paint
+                                switchSize,
+                                paint
                             )
-                            paint.alpha = 255
-                        } else {
+                        }
+
+                        SwitchStatus.OFF -> {
+                            stationList.firstOrNull { it.entityId == point.entityId }?.status = 0
                             paint.color = ContextCompat.getColor(
-                                MyApplication.instance?.applicationContext!!,
+                                MyApplication.instance!!.applicationContext,
                                 R.color.common_switch_disable
                             )
+                            paint.alpha = 255
+                            canvas.drawCircle(
+                                x + switchSize / 2,
+                                y + switchSize / 2,
+                                switchSize,
+                                paint
+                            )
+                        }
+
+                        SwitchStatus.ALARM -> {
+                            stationList.firstOrNull { it.entityId == point.entityId }?.status = 2
+                            // 脉冲:颜色红/橙跳变 + alpha 呼吸 + 半径轻微伸缩 + 外光晕
+                            val t = pulsePhase
+                            val useRed = t < 0.5f
+                            val color = if (useRed) {
+                                // 如果你没有这些资源色,可以换成 Color.RED / 自定义颜色
+                                runCatching {
+                                    ContextCompat.getColor(
+                                        MyApplication.instance!!.applicationContext,
+                                        R.color.red_500
+                                    )
+                                }.getOrDefault(Color.parseColor("#EF4444"))
+                            } else {
+                                runCatching {
+                                    ContextCompat.getColor(
+                                        MyApplication.instance!!.applicationContext,
+                                        R.color.orange_500
+                                    )
+                                }.getOrDefault(Color.parseColor("#F97316"))
+                            }
+
+                            val alphaPulse =
+                                (160 + 95 * abs(sin(2f * Math.PI.toFloat() * t))).toInt()
+                            paint.color = color
+                            paint.alpha = alphaPulse
+
+                            val r = switchSize * (1.0f + 0.10f * sin(2f * Math.PI.toFloat() * t))
+                            canvas.drawCircle(
+                                x + switchSize / 2,
+                                y + switchSize / 2,
+                                r,
+                                paint
+                            )
+
+                            paint.alpha = (alphaPulse * 0.35f).toInt()
                             canvas.drawCircle(
                                 x + switchSize / 2,
                                 y + switchSize / 2,
-                                switchSize, paint
+                                r * 1.25f,
+                                paint
                             )
+
+                            paint.alpha = 255
+                        }
+
+                        SwitchStatus.UNKNOWN -> {
+                            // 未知:深灰
+                            paint.color = Color.DKGRAY
+                            paint.alpha = 200
+                            canvas.drawCircle(
+                                x + switchSize / 2,
+                                y + switchSize / 2,
+                                switchSize,
+                                paint
+                            )
+                            paint.alpha = 255
                         }
                     }
-                    // 然后画文字
+
+                    // ---- 文本(和你原来一致)----
                     paint.style = Paint.Style.FILL
                     paint.strokeWidth = 1f
                     paint.color = Color.WHITE
-                    paint.textSize = textSize  // 这里是「图内」的文字大小,后面会跟着缩放
-                    val textW = paint.measureText(point.pointSerialNumber ?: "")
-                    // 计算文本宽度和偏移
-                    val fontMetrics = paint.fontMetrics
-                    val textHeight = fontMetrics.bottom - fontMetrics.top
-
-                    // 计算绘制文本的起点(左下角)
+                    paint.textSize = textSize
+                    val text = point.pointSerialNumber ?: ""
+                    val textW = paint.measureText(text)
+                    val fm = paint.fontMetrics
+                    val textH = fm.bottom - fm.top
                     val textX = x + (switchSize - textW) / 2
-                    val textY = y + switchSize / 2 + (textHeight / 2 - fontMetrics.bottom)
-                    canvas.drawText(
-                        "${point.pointSerialNumber}",
-                        textX,
-                        textY,
-                        paint
-                    )
+                    val textY = y + switchSize / 2 + (textH / 2 - fm.bottom)
+                    canvas.drawText(text, textX, textY, paint)
                 }
             } finally {
                 canvas.restore()
@@ -250,25 +474,17 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
         } finally {
             inDraw = false
         }
-
     }
 
     /**
      * 根据序号查找点位的「图内坐标」。
-     * @return 对应的 PointF(x,y),找不到返回 null。
      */
     fun getPointPosition(serial: String): PointF? {
-        return stationList
-            .find { it.pointSerialNumber == serial }
-            ?.pos
+        return stationList.find { it.pointSerialNumber == serial }?.pos
     }
 
     /**
      * 选中一个点位:标记 isSelected 并定位到视图中心。
-     * @param serial      要选中的序号
-     * @param animated    是否平滑动画,false 则瞬间定位
-     * @param durationMs  动画时长(仅 animated = true 时生效)
-     * @return 是否成功找到了并处理
      */
     fun selectPoint(
         serial: String,
@@ -280,18 +496,12 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
         point.isSelected = true
         ThreadUtils.runOnMainDelayed(3000) {
             point.isSelected = false
-            stationList.replaceAll {
-                it.copy(isSelected = false)
-            }
+            stationList.replaceAll { it.copy(isSelected = false) }
             mapView?.postInvalidate()
         }
-        // 1. 更新状态(如果你要改变外观,比如高亮,记得在 draw() 里用 isSelected)
-        stationList.replaceAll {
-            it.copy(isSelected = (it.pointSerialNumber == serial))
-        }
+        stationList.replaceAll { it.copy(isSelected = (it.pointSerialNumber == serial)) }
         mapView?.postInvalidate()
 
-        // 2. 定位:调用 MapView 的 API
         mapView?.let { mv ->
             if (animated) {
                 mv.animateCenterOnPoint(point.pos.x, point.pos.y, durationMs, scale)
@@ -302,6 +512,46 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
         return true
     }
 
+    // ====== 状态解析 & 动画触发判定 ======
+
+    /** 预留:设备状态 */
+    private enum class SwitchStatus { ON, OFF, ALARM, UNKNOWN }
+
+    /**
+     * 统一解析设备状态:未来 SwitchBean 加字段时,只改这里。
+     * 现在:优先看 isAlarm(bean),否则用 enabled 判定;bean==null 记为 UNKNOWN。
+     */
+    private fun parseStatus(bean: DockBean.SwitchBean?): SwitchStatus {
+        if (bean == null) return SwitchStatus.UNKNOWN
+        if (isAlarm(bean)) return SwitchStatus.ALARM
+        return if (bean.enabled) SwitchStatus.ON else SwitchStatus.OFF
+    }
+
+    /**
+     * 是否告警(预留钩子):将来你在 SwitchBean 里加 type/statusCode 等,改这里即可。
+     * 当前默认:不告警。
+     */
+    private fun isAlarm(bean: DockBean.SwitchBean?): Boolean {
+        // TODO: 未来形如:
+        // return bean?.type == 2 || bean?.statusCode == ALARM
+        return false
+    }
+
+    /** 是否存在任意 ALARM 点位,用于决定是否跑动画帧 */
+    private fun hasAnyAlarm(
+        points: List<IsolationPoint>,
+        switches: List<DockBean.SwitchBean>?
+    ): Boolean {
+        if (switches == null) return false
+        for (p in points) {
+            val b = switches.find { it.idx == p.pointSerialNumber?.toInt() }
+            if (isAlarm(b)) return true
+        }
+        return false
+    }
+
+    // ====== 数据结构(保持你的原样)======
+
     data class IsolationPoint(
         val pos: PointF,
         val entityName: String,
@@ -309,5 +559,6 @@ class CustomSwitchStationLayer @JvmOverloads constructor(
         val entityId: Long,
         val pointSerialNumber: String?,
         var isSelected: Boolean,
+        var status: Int = 0
     )
 }

+ 8 - 1
app/src/main/java/com/onlylemi/mapview/library/MapView.java

@@ -55,6 +55,8 @@ public class MapView extends TextureView implements TextureView.SurfaceTextureLi
 
     private MapViewListener mapViewListener;
 
+    private int backgroundColor = Color.WHITE; // 默认白色
+
     public static final int TOUCH_STATE_NO = 0;
     public static final int TOUCH_STATE_SCROLL = 1;
     public static final int TOUCH_STATE_SCALE = 2;
@@ -74,6 +76,11 @@ public class MapView extends TextureView implements TextureView.SurfaceTextureLi
         init();
     }
 
+    public void setBackgroundColorInt(int color) {
+        this.backgroundColor = color;
+        refresh(); // 设置后刷新
+    }
+
     private void init() {
         setSurfaceTextureListener(this);
         setOpaque(true);
@@ -150,7 +157,7 @@ public class MapView extends TextureView implements TextureView.SurfaceTextureLi
         if (surface == null || !isMapLoadFinish) return;
         Canvas canvas = lockCanvas();
         if (canvas != null) {
-            canvas.drawColor(Color.WHITE);
+            canvas.drawColor(backgroundColor);
             for (MapBaseLayer layer : layers) {
                 if (layer.isVisible) {
                     layer.draw(canvas, currentMatrix, currentZoom, currentRotateDegrees);

+ 6 - 0
app/src/main/res/drawable/bg_switch_alarm.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/common_radius_small" />
+    <solid android:color="@color/red_500" />
+</shape>

+ 6 - 0
app/src/main/res/drawable/bg_switch_off.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/common_radius_small" />
+    <solid android:color="@color/common_switch_disable" />
+</shape>

+ 6 - 0
app/src/main/res/drawable/bg_switch_on.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/common_radius_small" />
+    <solid android:color="@color/common_switch_enable" />
+</shape>

+ 4 - 1
app/src/main/res/drawable/common_layout_bg.xml

@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
-    <solid android:color="@color/white" />
+    <solid android:color="@color/color_map_base" />
+    <stroke
+        android:width="1dp"
+        android:color="@color/color_map_stroke" />
     <corners android:radius="@dimen/common_radius_small" />
 </shape>

+ 15 - 0
app/src/main/res/drawable/icon_chevron_left.xml

@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#314158"
+        android:pathData="M12,12 m-10,0 a10,10 0 1,0 20,0 a10,10 0 1,0 -20,0" />
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z" />
+    <!-- 圆形背景 -->
+</vector>

+ 54 - 83
app/src/main/res/layout/activity_switch_status.xml

@@ -7,109 +7,80 @@
     android:background="@mipmap/main_bg"
     android:divider="@drawable/divider_horizontal"
     android:orientation="vertical"
-    android:padding="@dimen/common_spacing"
     android:showDividers="middle">
 
-    <com.grkj.iscs_mars.view.widget.CommonBtn
-        android:id="@+id/cb_back"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_above="@id/tv_tip"
-        android:layout_alignParentRight="true"
-        android:layout_marginBottom="@dimen/common_spacing_small"
-        app:btn_bg="@drawable/common_btn_blue_bg"
-        app:btn_name="@string/back" />
-
-    <LinearLayout
+    <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:divider="@drawable/divider_horizontal"
         android:orientation="horizontal"
         android:showDividers="middle">
 
-        <com.google.android.material.card.MaterialCardView
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="2"
-            android:stateListAnimator="@null"
-            app:cardCornerRadius="@dimen/common_radius_small"
-            app:cardElevation="0dp"
-            app:cardPreventCornerOverlap="false"
-            app:cardUseCompatPadding="false"
-            app:strokeWidth="0dp">
-
-            <com.onlylemi.mapview.library.MapView
-                android:id="@+id/mapview"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent" />
-        </com.google.android.material.card.MaterialCardView>
+        <com.onlylemi.mapview.library.MapView
+            android:id="@+id/mapview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
 
-        <LinearLayout
-            android:layout_width="0dp"
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:background="@drawable/common_layout_bg"
-            android:orientation="vertical"
-            android:padding="@dimen/common_spacing">
+            android:layout_gravity="right">
 
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="@string/switch_information"
-                android:textColor="@color/black"
-                android:textSize="@dimen/common_text_size"
-                android:textStyle="bold" />
 
             <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/common_spacing"
-                android:orientation="horizontal">
+                android:id="@+id/point_list"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginLeft="20dp"
+                android:background="@drawable/common_layout_bg"
+                android:orientation="vertical"
+                android:padding="@dimen/common_spacing"
+                android:minWidth="200dp"
+                android:visibility="gone"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="@id/cb_show"
+                app:layout_constraintTop_toTopOf="parent">
 
-                <TextView
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:gravity="center"
-                    android:paddingVertical="@dimen/common_spacing_small"
-                    android:text="@string/index_number"
-                    android:textColor="@color/black"
-                    android:textSize="@dimen/common_text_size" />
 
-                <TextView
-                    android:layout_width="0dp"
+                <com.grkj.iscs_mars.view.widget.CommonBtn
+                    android:id="@+id/cb_back"
+                    android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:gravity="center"
-                    android:text="@string/name"
-                    android:paddingVertical="@dimen/common_spacing_small"
-                    android:textColor="@color/black"
-                    android:textSize="@dimen/common_text_size" />
+                    android:layout_above="@id/tv_tip"
+                    android:layout_gravity="center_horizontal"
+                    app:btn_bg="@drawable/common_btn_blue_bg"
+                    app:btn_name="@string/back" />
 
                 <TextView
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:gravity="center"
-                    android:paddingVertical="@dimen/common_spacing_small"
-                    android:text="@string/switch_status"
-                    android:textColor="@color/black"
-                    android:textSize="@dimen/common_text_size" />
-                <TextView
-                    android:layout_width="0dp"
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_weight="1"
+                    android:layout_gravity="center_horizontal"
                     android:gravity="center"
-                    android:paddingVertical="@dimen/common_spacing_small"
-                    android:text="@string/operation"
-                    android:textColor="@color/black"
-                    android:textSize="@dimen/common_text_size" />
+                    android:layout_marginVertical="@dimen/common_spacing"
+                    android:text="@string/switch_information"
+                    android:textColor="@color/white"
+                    android:textSize="@dimen/common_text_size"
+                    android:textStyle="bold" />
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/rv_list"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:minWidth="200dp"
+                    android:paddingHorizontal="10dp" />
             </LinearLayout>
 
-            <androidx.recyclerview.widget.RecyclerView
-                android:id="@+id/rv_list"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent" />
-        </LinearLayout>
-    </LinearLayout>
+            <ImageView
+                android:id="@+id/cb_show"
+                android:layout_width="40dp"
+                android:layout_height="40dp"
+                android:src="@drawable/icon_chevron_left"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </FrameLayout>
 </LinearLayout>

+ 57 - 0
app/src/main/res/layout/dialog_switch_info.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/common_layout_bg"
+        android:divider="@drawable/divider_vertical"
+        android:orientation="vertical"
+        android:padding="@dimen/common_spacing"
+        android:showDividers="middle">
+
+        <TextView
+            android:id="@+id/switch_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/white"
+            android:textSize="@dimen/common_text_size_big"
+            android:textStyle="bold"
+            tools:text="Switch station 5" />
+
+        <TextView
+            android:id="@+id/switch_id"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/white"
+            android:textSize="@dimen/common_text_size"
+            tools:text="ID:" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/switch_status_tv"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/switch_status_tv"
+                android:textColor="@color/white"
+                android:textSize="@dimen/common_text_size" />
+
+            <TextView
+                android:id="@+id/switch_status"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:background="@drawable/bg_switch_off"
+                android:textColor="@color/white"
+                android:textSize="@dimen/common_text_size"
+                android:paddingHorizontal="@dimen/common_spacing_small"
+                android:paddingVertical="@dimen/common_spacing_smallest"
+                android:textStyle="bold"
+                tools:text="OFF" />
+        </LinearLayout>
+    </LinearLayout>
+</layout>

+ 51 - 47
app/src/main/res/layout/item_switch.xml

@@ -1,54 +1,58 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:gravity="center_vertical"
-    android:orientation="horizontal">
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
 
-    <TextView
-        android:id="@+id/index"
-        android:layout_width="0dp"
+    <LinearLayout
+        android:id="@+id/root"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:gravity="center"
-        android:paddingVertical="@dimen/common_spacing_small"
-        android:textColor="@color/black"
-        android:textSize="@dimen/common_text_size" />
+        android:background="@drawable/common_layout_bg"
+        android:divider="@drawable/divider_vertical"
+        android:orientation="vertical"
+        android:padding="@dimen/common_spacing"
+        android:showDividers="middle">
 
-    <TextView
-        android:id="@+id/point_name"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:gravity="center"
-        android:paddingVertical="@dimen/common_spacing_small"
-        android:textColor="@color/black"
-        android:textSize="@dimen/common_text_size" />
-
-    <TextView
-        android:id="@+id/status"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:gravity="center"
-        android:paddingVertical="@dimen/common_spacing_small"
-        android:textColor="@color/black"
-        android:textSize="@dimen/common_text_size" />
-
-    <FrameLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:paddingVertical="@dimen/common_spacing_small">
+        <TextView
+            android:id="@+id/switch_name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/white"
+            android:textSize="@dimen/common_text_size_big"
+            android:textStyle="bold"
+            tools:text="Switch station 5" />
 
-        <com.grkj.iscs_mars.view.widget.CommonBtn
-            android:id="@+id/position"
+        <TextView
+            android:id="@+id/switch_id"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:gravity="center"
-            app:btn_bg="@drawable/common_btn_blue_bg"
-            app:btn_name="@string/positioning" />
-    </FrameLayout>
-</LinearLayout>
+            android:textColor="@color/white"
+            android:textSize="@dimen/common_text_size"
+            tools:text="ID:" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/switch_status_tv"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/switch_status_tv"
+                android:textColor="@color/white"
+                android:textSize="@dimen/common_text_size" />
+
+            <TextView
+                android:id="@+id/switch_status"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:background="@drawable/bg_switch_off"
+                android:textColor="@color/white"
+                android:textSize="@dimen/common_text_size"
+                android:paddingHorizontal="@dimen/common_spacing_small"
+                android:paddingVertical="@dimen/common_spacing_smallest"
+                android:textStyle="bold"
+                tools:text="OFF" />
+        </LinearLayout>
+    </LinearLayout>
+</layout>

+ 0 - 5
app/src/main/res/values-en/colors.xml

@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <color name="common_switch_enable">#91ce93</color>
-    <color name="common_switch_disable">#f0f0f0</color>
-</resources>

+ 5 - 0
app/src/main/res/values-en/strings.xml

@@ -396,6 +396,7 @@
     <string name="name">Name</string>
     <string name="switch_open">Open</string>
     <string name="switch_close">Close</string>
+    <string name="switch_alarm">Alarm</string>
     <string name="positioning">Positioning</string>
     <string name="operation">Operation</string>
     <string name="ticket_get_failed">Ticket get failed,Please reposition the key</string>
@@ -406,4 +407,8 @@
     <string name="key_not_enough_wait_or_take_on_another">There is no key available for distribution in the current cabinet. Please obtain the key from other cabinets or wait for other operations to return the key.</string>
     <string name="key_power_low_wait_or_take_on_another">The current power of the cabinet key is low. Please wait for 15 minutes or get it from another cabinet.</string>
     <string name="lock_enough_but_key_enough_but_key_power_down">The current cabinet can distribute %1$d padlocks but The current power of the cabinet key is low. Please obtain keys from other cabinets or wait for other jobs to return the keys. \nAre you sure you want to perform the operation?</string>
+    <string name="show_point_list">Show</string>
+    <string name="hide_point_list">Hide</string>
+    <string name="switch_id">ID:%1$s</string>
+    <string name="switch_status_tv">Switch Status:</string>
 </resources>

+ 0 - 5
app/src/main/res/values-zh/colors.xml

@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <color name="common_switch_enable">#91ce93</color>
-    <color name="common_switch_disable">#f0f0f0</color>
-</resources>

+ 5 - 0
app/src/main/res/values-zh/strings.xml

@@ -396,6 +396,7 @@
     <string name="name">名称</string>
     <string name="switch_open">开启</string>
     <string name="switch_close">关闭</string>
+    <string name="switch_alarm">警告</string>
     <string name="positioning">定位</string>
     <string name="operation">操作</string>
     <string name="ticket_get_failed">作业票获取失败,请重新放置钥匙</string>
@@ -406,4 +407,8 @@
     <string name="key_not_enough_wait_or_take_on_another">当前机柜无可派发钥匙,请从其他机柜获取或等待其他作业归还钥匙。</string>
     <string name="key_power_low_wait_or_take_on_another">当前机柜钥匙电量较低,请等待15分钟或从其他机柜获取。</string>
     <string name="lock_enough_but_key_enough_but_key_power_down">当前机柜可派发%1$d把挂锁,但是当前机柜钥匙电量较低,请从其他机柜获取钥匙或等待其他作业归还钥匙。\n确定要执行操作吗?</string>
+    <string name="show_point_list">显示</string>
+    <string name="hide_point_list">隐藏</string>
+    <string name="switch_id">编号:%1$s</string>
+    <string name="switch_status_tv">开关状态:</string>
 </resources>

+ 7 - 2
app/src/main/res/values/colors.xml

@@ -28,6 +28,9 @@
     <color name="selectable_input_prefix">#bd3124</color>
     <color name="switch_track_on">#298EFF</color>
     <color name="switch_track_off">#E9E9E9</color>
+    <color name="red_500">#EF4444</color>
+    <color name="orange_500">#F97316</color>
+    <color name="gray_600">#4B5563</color>
     <color name="dialog_card_login_bg">#990E57EA</color>
     <color name="home_menu_bg">#4D2B7AE9</color>
     <color name="common_btn_red_bg">#80FF0000</color>
@@ -41,9 +44,11 @@
     <color name="item_rv_step_bg_done">#1d7153</color>
     <color name="item_rv_step_bg_doing">#838a53</color>
     <color name="item_rv_step_bg_ready">#B3FFFFFF</color>
-    <color name="common_switch_enable">#91ce93</color>
-    <color name="common_switch_disable">#f0f0f0</color>
+    <color name="common_switch_enable">#22C55E</color>
+    <color name="common_switch_disable">#4B5563</color>
     <color name="color_d7d2d2">#d7d2d2</color>
+    <color name="color_map_base">#1d293d</color>
+    <color name="color_map_stroke">#45556c</color>
     <color name="dialogxColorBlue">#2196F3</color>
     <color name="common_transparent_half">#33ffffff</color>
 </resources>

+ 5 - 0
app/src/main/res/values/strings.xml

@@ -396,6 +396,7 @@
     <string name="name">名称</string>
     <string name="switch_open">开启</string>
     <string name="switch_close">关闭</string>
+    <string name="switch_alarm">警告</string>
     <string name="positioning">定位</string>
     <string name="operation">操作</string>
     <string name="ticket_get_failed">作业票获取失败,请重新放置钥匙</string>
@@ -406,4 +407,8 @@
     <string name="key_not_enough_wait_or_take_on_another">当前机柜无可派发钥匙,请从其他机柜获取或等待其他作业归还钥匙。</string>
     <string name="key_power_low_wait_or_take_on_another">当前机柜钥匙电量较低,请等待15分钟或从其他机柜获取。</string>
     <string name="lock_enough_but_key_enough_but_key_power_down">当前机柜可派发%1$d把挂锁,但是当前机柜钥匙电量较低,请从其他机柜获取钥匙或等待其他作业归还钥匙。\n确定要执行操作吗?</string>
+    <string name="show_point_list">显示</string>
+    <string name="hide_point_list">隐藏</string>
+    <string name="switch_id">编号:%1$s</string>
+    <string name="switch_status_tv">开关状态:</string>
 </resources>