feat(activity): 为子设备 SlaveActivity 添加调料秤重量增加高亮提醒功能

检测本机秤稳定后重量增加超过10g,高亮对应格子(红底白字),15秒后自动恢复
This commit is contained in:
2026-04-20 09:08:44 +08:00
parent 87bf11454c
commit 193acff1e0
3 changed files with 112 additions and 1 deletions
@@ -1,12 +1,14 @@
package com.shuwei.dish.match.adapter package com.shuwei.dish.match.adapter
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.annotation.SuppressLint
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.dish.match.R
import com.shuwei.dish.match.databinding.ListItemScale22RowBinding import com.shuwei.dish.match.databinding.ListItemScale22RowBinding
import com.shuwei.dish.match.scale.ScaleData import com.shuwei.dish.match.scale.ScaleData
import com.shuwei.dish.match.scale.ScaleDeviceConfig import com.shuwei.dish.match.scale.ScaleDeviceConfig
@@ -25,6 +27,9 @@ class Scale18GridAdapter : BaseQuickAdapter<ScaleData, Scale18GridAdapter.VH>(mu
/** 用 address=0 的哨兵表示空位 */ /** 用 address=0 的哨兵表示空位 */
private val empty = ScaleData("", 0, 0.0, -1, 0) private val empty = ScaleData("", 0, 0.0, -1, 0)
/** 当前高亮的格子位置,-1 表示无高亮 */
private var highlightedPosition: Int = -1
/** 点击回调,在 onCreateViewHolder 中绑定,不受 notifyItemChanged 影响 */ /** 点击回调,在 onCreateViewHolder 中绑定,不受 notifyItemChanged 影响 */
var onItemClick: ((ScaleData) -> Unit)? = null var onItemClick: ((ScaleData) -> Unit)? = null
@@ -91,6 +96,38 @@ class Scale18GridAdapter : BaseQuickAdapter<ScaleData, Scale18GridAdapter.VH>(mu
holder.b.tvAddr.text = if (valid) "${item!!.address}" else "" holder.b.tvAddr.text = if (valid) "${item!!.address}" else ""
holder.b.tvWeight.text = if (valid) "${item!!.weight}g" else "" holder.b.tvWeight.text = if (valid) "${item!!.weight}g" else ""
holder.b.tvState.text = if (valid) stateStr(item!!.state) else "" holder.b.tvState.text = if (valid) stateStr(item!!.state) else ""
// 高亮格子:红底白字;其余恢复默认
if (position == highlightedPosition) {
holder.b.root.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
val white = ContextCompat.getColor(holder.b.root.context, R.color.white)
holder.b.tvAddr.setTextColor(white)
holder.b.tvWeight.setTextColor(white)
holder.b.tvState.setTextColor(white)
} else {
holder.b.root.setBackgroundResource(R.drawable.shape_scale_cell)
val normal = ContextCompat.getColor(holder.b.root.context, R.color.home_title)
holder.b.tvAddr.setTextColor(normal)
holder.b.tvWeight.setTextColor(normal)
holder.b.tvState.setTextColor(normal)
}
}
/**
* 高亮指定位置的格子(红底白字),同时清除上一个高亮
* @param position 要高亮的格子索引
*/
fun highlightPosition(position: Int) {
val prev = highlightedPosition
highlightedPosition = position
if (prev >= 0) notifyItemChanged(prev)
if (position >= 0) notifyItemChanged(position)
}
/**
* 清除所有高亮,恢复默认样式
*/
fun clearHighlight() {
highlightPosition(-1)
} }
private fun stateStr(state: Int) = when (state) { private fun stateStr(state: Int) = when (state) {
@@ -4,10 +4,12 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.flexbox.FlexboxLayoutManager
import com.shuwei.dish.match.R
import com.shuwei.dish.match.databinding.ListItemScale22RowBinding import com.shuwei.dish.match.databinding.ListItemScale22RowBinding
import com.shuwei.dish.match.scale.ScaleData import com.shuwei.dish.match.scale.ScaleData
import com.shuwei.dish.match.scale.ScaleDeviceConfig import com.shuwei.dish.match.scale.ScaleDeviceConfig
@@ -32,6 +34,9 @@ class Scale22GridAdapter(
/** 用 address=0 的哨兵表示空位 */ /** 用 address=0 的哨兵表示空位 */
private val empty = ScaleData("", 0, 0.0, -1, 0) private val empty = ScaleData("", 0, 0.0, -1, 0)
/** 当前高亮的格子位置,-1 表示无高亮 */
private var highlightedPosition: Int = -1
/** 点击回调,在 onCreateViewHolder 中绑定,不受 notifyItemChanged 影响 */ /** 点击回调,在 onCreateViewHolder 中绑定,不受 notifyItemChanged 影响 */
var onItemClick: ((ScaleData) -> Unit)? = null var onItemClick: ((ScaleData) -> Unit)? = null
@@ -93,6 +98,38 @@ class Scale22GridAdapter(
holder.b.tvAddr.text = if (valid) "${item!!.address}" else "" holder.b.tvAddr.text = if (valid) "${item!!.address}" else ""
holder.b.tvWeight.text = if (valid) "${item!!.weight}g" else "" holder.b.tvWeight.text = if (valid) "${item!!.weight}g" else ""
holder.b.tvState.text = if (valid) stateStr(item!!.state) else "" holder.b.tvState.text = if (valid) stateStr(item!!.state) else ""
// 高亮格子:红底白字;其余恢复默认
if (position == highlightedPosition) {
holder.b.root.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
val white = ContextCompat.getColor(holder.b.root.context, R.color.white)
holder.b.tvAddr.setTextColor(white)
holder.b.tvWeight.setTextColor(white)
holder.b.tvState.setTextColor(white)
} else {
holder.b.root.setBackgroundResource(R.drawable.shape_scale_cell)
val normal = ContextCompat.getColor(holder.b.root.context, R.color.home_title)
holder.b.tvAddr.setTextColor(normal)
holder.b.tvWeight.setTextColor(normal)
holder.b.tvState.setTextColor(normal)
}
}
/**
* 高亮指定位置的格子(红底白字),同时清除上一个高亮
* @param position 要高亮的格子索引
*/
fun highlightPosition(position: Int) {
val prev = highlightedPosition
highlightedPosition = position
if (prev >= 0) notifyItemChanged(prev)
if (position >= 0) notifyItemChanged(position)
}
/**
* 清除所有高亮,恢复默认样式
*/
fun clearHighlight() {
highlightPosition(-1)
} }
private fun stateStr(state: Int) = when (state) { private fun stateStr(state: Int) = when (state) {
@@ -2,6 +2,8 @@ package com.shuwei.dish.match.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@@ -45,6 +47,13 @@ class SlaveActivity : BaseActivity() {
/** 已连接的主设备数量(有连接即代表主设备在线) */ /** 已连接的主设备数量(有连接即代表主设备在线) */
private var masterConnected = false private var masterConnected = false
/** 各秤地址的上一次重量快照,用于计算 delta */
private val weightSnapshot = mutableMapOf<Int, Double>()
/** 主线程 Handler,用于延迟清除高亮 */
private val mainHandler = Handler(Looper.getMainLooper())
/** 当前高亮清除任务,新高亮触发时取消旧任务 */
private var clearHighlightRunnable: Runnable? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivitySlaveBinding.inflate(layoutInflater) binding = ActivitySlaveBinding.inflate(layoutInflater)
@@ -134,9 +143,19 @@ class SlaveActivity : BaseActivity() {
/** /**
* 监听本机 WeightUtil 回调,实时刷新秤列表 * 监听本机 WeightUtil 回调,实时刷新秤列表
* 22/18个秤走网格 adapter,其余走线性列表 * 22/18个秤走网格 adapter,其余走线性列表
* 秤稳定且重量增加超过阈值时,高亮对应格子,15秒后自动恢复
*/ */
private fun listenLocalScales() { private fun listenLocalScales() {
WeightUtil.addWeightListener(TAG) { address, state, weight -> WeightUtil.addWeightListener(TAG) { address, state, weight ->
// 重量快照与高亮判断(在回调线程计算,不涉及 UI)
val baseline = weightSnapshot[address]
val shouldHighlight = baseline != null
&& state == WeightUtil.STATE_STABLE
&& (weight - baseline) > WEIGHT_DELTA_THRESHOLD
if (baseline == null || state == WeightUtil.STATE_STABLE) {
weightSnapshot[address] = weight
}
runOnUiThread { runOnUiThread {
when { when {
scale22Adapter != null -> { scale22Adapter != null -> {
@@ -167,6 +186,19 @@ class SlaveActivity : BaseActivity() {
linearAdapter.submitList(list) linearAdapter.submitList(list)
} }
} }
// 触发格子高亮
if (shouldHighlight) {
clearHighlightRunnable?.let { mainHandler.removeCallbacks(it) }
val pos22 = ScaleDeviceConfig.SCALE_ORDER_22.indexOf(address)
val pos18 = ScaleDeviceConfig.SCALE_ORDER_18.indexOf(address)
scale22Adapter?.highlightPosition(pos22)
scale18Adapter?.highlightPosition(pos18)
clearHighlightRunnable = Runnable {
scale22Adapter?.clearHighlight()
scale18Adapter?.clearHighlight()
}.also { mainHandler.postDelayed(it, HIGHLIGHT_DURATION_MS) }
}
} }
} }
} }
@@ -186,11 +218,16 @@ class SlaveActivity : BaseActivity() {
override fun onDestroy() { override fun onDestroy() {
ScaleServiceManager.onMasterConnectionChanged = null ScaleServiceManager.onMasterConnectionChanged = null
WeightUtil.removeWeightListener(TAG) WeightUtil.removeWeightListener(TAG)
clearHighlightRunnable?.let { mainHandler.removeCallbacks(it) }
super.onDestroy() super.onDestroy()
} }
companion object { companion object {
const val TAG = "SlaveActivity" const val TAG = "SlaveActivity"
/** 重量增加阈值(克),超过此值才触发高亮 */
private const val WEIGHT_DELTA_THRESHOLD = 10.0
/** 高亮自动恢复时间(毫秒) */
private const val HIGHLIGHT_DURATION_MS = 15_000L
} }
/** /**