feat(fragment): 实现调料秤重量增加高亮提醒功能
主设备订阅 allScales StateFlow 检测所有子设备秤重量变化, 超过10g且秤稳定时高亮对应格子,15秒后自动恢复
This commit is contained in:
@@ -3,9 +3,11 @@ package com.shuwei.dish.match.adapter
|
|||||||
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.shuwei.dish.match.R
|
||||||
import com.shuwei.dish.match.databinding.ListItemSeasoningCellBinding
|
import com.shuwei.dish.match.databinding.ListItemSeasoningCellBinding
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
@@ -21,6 +23,9 @@ class Seasoning18GridAdapter : BaseQuickAdapter<SeasoningEntity, Seasoning18Grid
|
|||||||
/** 空位哨兵,sort=-1 表示无效 */
|
/** 空位哨兵,sort=-1 表示无效 */
|
||||||
private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 }
|
private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 }
|
||||||
|
|
||||||
|
/** 当前高亮的格子位置,-1 表示无高亮 */
|
||||||
|
private var highlightedPosition: Int = -1
|
||||||
|
|
||||||
/** 点击回调,预留,暂不使用 */
|
/** 点击回调,预留,暂不使用 */
|
||||||
var onItemClick: ((SeasoningEntity, Int) -> Unit)? = null
|
var onItemClick: ((SeasoningEntity, Int) -> Unit)? = null
|
||||||
|
|
||||||
@@ -57,6 +62,32 @@ class Seasoning18GridAdapter : BaseQuickAdapter<SeasoningEntity, Seasoning18Grid
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) {
|
override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) {
|
||||||
holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-"
|
holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-"
|
||||||
|
// 高亮格子:红底白字;其余恢复默认
|
||||||
|
if (position == highlightedPosition) {
|
||||||
|
holder.b.tvGoodsName.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
||||||
|
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.white))
|
||||||
|
} else {
|
||||||
|
holder.b.tvGoodsName.setBackgroundResource(R.drawable.shape_scale_cell)
|
||||||
|
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.home_title))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高亮指定位置的格子(红底白字),同时清除上一个高亮
|
||||||
|
* @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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package com.shuwei.dish.match.adapter
|
|||||||
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.ListItemSeasoningCellBinding
|
import com.shuwei.dish.match.databinding.ListItemSeasoningCellBinding
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
@@ -28,6 +30,9 @@ class Seasoning22GridAdapter(
|
|||||||
/** 空位哨兵,sort=-1 表示无效 */
|
/** 空位哨兵,sort=-1 表示无效 */
|
||||||
private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 }
|
private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 }
|
||||||
|
|
||||||
|
/** 当前高亮的格子位置,-1 表示无高亮 */
|
||||||
|
private var highlightedPosition: Int = -1
|
||||||
|
|
||||||
/** 点击回调,预留,暂不使用 */
|
/** 点击回调,预留,暂不使用 */
|
||||||
var onItemClick: ((SeasoningEntity, Int) -> Unit)? = null
|
var onItemClick: ((SeasoningEntity, Int) -> Unit)? = null
|
||||||
|
|
||||||
@@ -60,6 +65,32 @@ class Seasoning22GridAdapter(
|
|||||||
|
|
||||||
override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) {
|
override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) {
|
||||||
holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-"
|
holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-"
|
||||||
|
// 高亮格子:红底白字;其余恢复默认
|
||||||
|
if (position == highlightedPosition) {
|
||||||
|
holder.b.tvGoodsName.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
||||||
|
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.white))
|
||||||
|
} else {
|
||||||
|
holder.b.tvGoodsName.setBackgroundResource(R.drawable.shape_scale_cell)
|
||||||
|
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.home_title))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高亮指定位置的格子(红底白字),同时清除上一个高亮
|
||||||
|
* @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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.shuwei.dish.match.scale
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 秤事件数据类,用于子设备向主设备发送非重量类通知
|
||||||
|
* @param type 事件类型,参见 companion object 中的常量
|
||||||
|
* @param deviceId 发送方设备 ID
|
||||||
|
* @param address 触发事件的秤地址
|
||||||
|
* @param delta 重量变化量(克),仅 TYPE_SEASONING_ADDED 时有意义
|
||||||
|
*/
|
||||||
|
data class ScaleEvent(
|
||||||
|
val type: String,
|
||||||
|
val deviceId: String,
|
||||||
|
val address: Int,
|
||||||
|
val delta: Double = 0.0
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
/** 调料添加事件:某秤重量增加超过阈值 */
|
||||||
|
const val TYPE_SEASONING_ADDED = "seasoning_added"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,26 @@ object ScaleServiceManager {
|
|||||||
val allScales: StateFlow<Map<String, ScaleData>>?
|
val allScales: StateFlow<Map<String, ScaleData>>?
|
||||||
get() = aggregator?.allScales
|
get() = aggregator?.allScales
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到子设备秤事件(如调料添加)时的回调,仅主设备有效
|
||||||
|
* 在子线程中调用,需自行切换到主线程更新 UI
|
||||||
|
*/
|
||||||
|
private var _onScaleEvent: ((ScaleEvent) -> Unit)? = null
|
||||||
|
var onScaleEvent: ((ScaleEvent) -> Unit)?
|
||||||
|
get() = _onScaleEvent
|
||||||
|
set(value) {
|
||||||
|
_onScaleEvent = value
|
||||||
|
wsClient?.onScaleEvent = value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向主设备广播秤事件(子设备调用)
|
||||||
|
* @param event 要广播的事件
|
||||||
|
*/
|
||||||
|
fun broadcastEvent(event: ScaleEvent) {
|
||||||
|
wsServer?.broadcastEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主设备连接状态回调(仅子设备使用)
|
* 主设备连接状态回调(仅子设备使用)
|
||||||
* 设置后会立即转发给 wsServer;需在 start() 之后设置才能生效
|
* 设置后会立即转发给 wsServer;需在 start() 之后设置才能生效
|
||||||
@@ -100,6 +120,7 @@ object ScaleServiceManager {
|
|||||||
// WebSocket 客户端
|
// WebSocket 客户端
|
||||||
val client = ScaleWebSocketClient().also {
|
val client = ScaleWebSocketClient().also {
|
||||||
it.onScaleData = { data -> aggregator?.onRemoteScaleData(data) }
|
it.onScaleData = { data -> aggregator?.onRemoteScaleData(data) }
|
||||||
|
it.onScaleEvent = { event -> _onScaleEvent?.invoke(event) }
|
||||||
it.onDeviceDisconnected = { remoteId -> aggregator?.removeDevice(remoteId) }
|
it.onDeviceDisconnected = { remoteId -> aggregator?.removeDevice(remoteId) }
|
||||||
}
|
}
|
||||||
wsClient = client
|
wsClient = client
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ class ScaleWebSocketClient {
|
|||||||
/** 收到秤数据时的回调,在子线程调用 */
|
/** 收到秤数据时的回调,在子线程调用 */
|
||||||
var onScaleData: ((data: ScaleData) -> Unit)? = null
|
var onScaleData: ((data: ScaleData) -> Unit)? = null
|
||||||
|
|
||||||
|
/** 收到秤事件(如调料添加通知)时的回调,在子线程调用 */
|
||||||
|
var onScaleEvent: ((event: ScaleEvent) -> Unit)? = null
|
||||||
|
|
||||||
/** 设备断线时的回调(连接失败或关闭),在子线程调用 */
|
/** 设备断线时的回调(连接失败或关闭),在子线程调用 */
|
||||||
var onDeviceDisconnected: ((deviceId: String) -> Unit)? = null
|
var onDeviceDisconnected: ((deviceId: String) -> Unit)? = null
|
||||||
|
|
||||||
@@ -80,10 +83,17 @@ class ScaleWebSocketClient {
|
|||||||
|
|
||||||
override fun onMessage(webSocket: WebSocket, text: String) {
|
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||||
try {
|
try {
|
||||||
|
// 通过 type 字段区分 ScaleEvent 与 ScaleData
|
||||||
|
val raw = gson.fromJson(text, Map::class.java)
|
||||||
|
if (raw.containsKey("type")) {
|
||||||
|
val event = gson.fromJson(text, ScaleEvent::class.java)
|
||||||
|
onScaleEvent?.invoke(event)
|
||||||
|
} else {
|
||||||
val data = gson.fromJson(text, ScaleData::class.java)
|
val data = gson.fromJson(text, ScaleData::class.java)
|
||||||
onScaleData?.invoke(data)
|
onScaleData?.invoke(data)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w(TAG, "解析秤数据失败: ${e.message}")
|
Log.w(TAG, "解析消息失败: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,14 @@ class ScaleWebSocketServer(private val deviceId: String, private val context: Co
|
|||||||
Log.d(TAG, "WebSocket 服务端已停止")
|
Log.d(TAG, "WebSocket 服务端已停止")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 向所有已连接的主设备广播秤事件(如调料添加通知)
|
||||||
|
* @param event 要广播的事件
|
||||||
|
*/
|
||||||
|
fun broadcastEvent(event: ScaleEvent) {
|
||||||
|
broadcast(gson.toJson(event))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 向所有已连接客户端广播消息
|
* 向所有已连接客户端广播消息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.shuwei.dish.match.ui.fragment
|
package com.shuwei.dish.match.ui.fragment
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import com.google.android.flexbox.AlignItems
|
import com.google.android.flexbox.AlignItems
|
||||||
import com.google.android.flexbox.FlexDirection
|
import com.google.android.flexbox.FlexDirection
|
||||||
@@ -10,13 +14,28 @@ import com.google.android.flexbox.FlexboxLayoutManager
|
|||||||
import com.shuwei.dish.match.adapter.Seasoning18GridAdapter
|
import com.shuwei.dish.match.adapter.Seasoning18GridAdapter
|
||||||
import com.shuwei.dish.match.adapter.Seasoning22GridAdapter
|
import com.shuwei.dish.match.adapter.Seasoning22GridAdapter
|
||||||
import com.shuwei.dish.match.base.BaseFragment
|
import com.shuwei.dish.match.base.BaseFragment
|
||||||
|
import com.shuwei.dish.match.base.DeviceRole
|
||||||
|
import com.shuwei.dish.match.base.GlobalData
|
||||||
import com.shuwei.dish.match.databinding.FragmentSeasoningConfigBinding
|
import com.shuwei.dish.match.databinding.FragmentSeasoningConfigBinding
|
||||||
import com.shuwei.dish.match.dialog.SeasoningSelectDialog
|
import com.shuwei.dish.match.dialog.SeasoningSelectDialog
|
||||||
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
|
import com.shuwei.dish.match.scale.ScaleEvent
|
||||||
|
import com.shuwei.dish.match.scale.ScaleServiceManager
|
||||||
import com.shuwei.dish.match.ui.SettingActivity
|
import com.shuwei.dish.match.ui.SettingActivity
|
||||||
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "SeasoningConfigFragment"
|
||||||
|
/** 重量增加阈值(克),超过此值才触发高亮 */
|
||||||
|
private const val WEIGHT_DELTA_THRESHOLD = 10.0
|
||||||
|
/** 高亮自动恢复时间(毫秒) */
|
||||||
|
private const val HIGHLIGHT_DURATION_MS = 15_000L
|
||||||
|
}
|
||||||
|
|
||||||
override fun inflateBinding(
|
override fun inflateBinding(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?
|
container: ViewGroup?
|
||||||
@@ -28,11 +47,19 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
|||||||
private val adapter22 by lazy { buildAdapter22() }
|
private val adapter22 by lazy { buildAdapter22() }
|
||||||
private val adapter18 by lazy { Seasoning18GridAdapter() }
|
private val adapter18 by lazy { Seasoning18GridAdapter() }
|
||||||
|
|
||||||
|
/** 各秤地址的上一次重量快照,用于计算 delta */
|
||||||
|
private val weightSnapshot = mutableMapOf<Int, Double>()
|
||||||
|
/** 主线程 Handler,用于延迟清除高亮 */
|
||||||
|
private val mainHandler = Handler(Looper.getMainLooper())
|
||||||
|
/** 当前高亮清除任务,新高亮触发时取消旧任务 */
|
||||||
|
private var clearHighlightRunnable: Runnable? = null
|
||||||
|
|
||||||
override fun initialize() {
|
override fun initialize() {
|
||||||
settingActivity = activity as SettingActivity
|
settingActivity = activity as SettingActivity
|
||||||
// initViewModel()
|
// initViewModel()
|
||||||
setupRecyclerViews()
|
setupRecyclerViews()
|
||||||
bindAdapterClicks()
|
bindAdapterClicks()
|
||||||
|
addWeightListener()
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,4 +135,144 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
|||||||
// adapter18.update(list ?: emptyList())
|
// adapter18.update(list ?: emptyList())
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册重量监听,检测重量增加超过阈值的秤,高亮对应格子并通知主设备
|
||||||
|
*
|
||||||
|
* 策略:
|
||||||
|
* - MASTER 设备:订阅 ScaleServiceManager.allScales StateFlow,覆盖本机+远端所有秤数据
|
||||||
|
* - SLAVE 设备:注册 WeightUtil 监听(仅本机秤)并向主设备广播事件
|
||||||
|
*
|
||||||
|
* 基准更新策略:
|
||||||
|
* - 首次收到数据:直接记录基准,不触发高亮
|
||||||
|
* - delta > 阈值:触发高亮,同时更新基准防止重复触发
|
||||||
|
* - STATE_STABLE:无条件更新基准(覆盖取走调料/归零等场景)
|
||||||
|
*/
|
||||||
|
private fun addWeightListener() {
|
||||||
|
Log.d(TAG, "addWeightListener: 注册监听, deviceId=${GlobalData.deviceId}, role=${GlobalData.deviceRole}")
|
||||||
|
|
||||||
|
if (GlobalData.deviceRole == DeviceRole.MASTER) {
|
||||||
|
// 主设备:通过 allScales StateFlow 观测所有秤(含远端子设备秤)
|
||||||
|
val allScalesFlow = ScaleServiceManager.allScales
|
||||||
|
if (allScalesFlow == null) {
|
||||||
|
Log.w(TAG, "allScales 为 null,主设备聚合器未启动")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
allScalesFlow.collect { scaleMap ->
|
||||||
|
scaleMap.forEach { (key, data) ->
|
||||||
|
val snapshotKey = data.address + data.deviceId.hashCode() * 1000
|
||||||
|
val baseline = weightSnapshot[snapshotKey]
|
||||||
|
if (baseline == null) {
|
||||||
|
// 首次收到:记录基准
|
||||||
|
weightSnapshot[snapshotKey] = data.weight
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
val delta = data.weight - baseline
|
||||||
|
Log.d(TAG, "allScales: key=$key address=${data.address} state=${data.state} weight=${data.weight} baseline=$baseline delta=$delta")
|
||||||
|
when {
|
||||||
|
// 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发
|
||||||
|
data.state == WeightUtil.STATE_STABLE && delta > WEIGHT_DELTA_THRESHOLD -> {
|
||||||
|
weightSnapshot[snapshotKey] = data.weight
|
||||||
|
Log.d(TAG, "allScales: 触发高亮 key=$key address=${data.address} delta=$delta")
|
||||||
|
triggerHighlight(data.deviceId, data.address, delta)
|
||||||
|
}
|
||||||
|
data.state == WeightUtil.STATE_STABLE -> {
|
||||||
|
weightSnapshot[snapshotKey] = data.weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 子设备:注册本机 WeightUtil 监听,触发后通过 broadcastEvent 通知主设备
|
||||||
|
WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
||||||
|
val baseline = weightSnapshot[address]
|
||||||
|
Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline")
|
||||||
|
|
||||||
|
if (baseline == null) {
|
||||||
|
weightSnapshot[address] = weight
|
||||||
|
return@addWeightListener
|
||||||
|
}
|
||||||
|
|
||||||
|
val delta = weight - baseline
|
||||||
|
Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline delta=$delta")
|
||||||
|
|
||||||
|
when {
|
||||||
|
// 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发
|
||||||
|
state == WeightUtil.STATE_STABLE && delta > WEIGHT_DELTA_THRESHOLD -> {
|
||||||
|
weightSnapshot[address] = weight
|
||||||
|
Log.d(TAG, "onWeight: 触发高亮 address=$address delta=$delta")
|
||||||
|
triggerHighlight(GlobalData.deviceId, address, delta)
|
||||||
|
}
|
||||||
|
state == WeightUtil.STATE_STABLE -> {
|
||||||
|
weightSnapshot[address] = weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据来源设备ID和秤地址触发对应格子高亮,15 秒后自动恢复
|
||||||
|
* 通过 sourceDeviceId 精确匹配 SCALE_ORDER,解决 18格/22格 地址重叠问题
|
||||||
|
* @param sourceDeviceId 产生重量变化的设备 ID
|
||||||
|
* @param address 秤硬件地址
|
||||||
|
* @param delta 重量变化量(克)
|
||||||
|
*/
|
||||||
|
private fun triggerHighlight(sourceDeviceId: String, address: Int, delta: Double) {
|
||||||
|
// 根据来源设备决定查哪个列表,而非查本机设备ID
|
||||||
|
val pos22 = if (sourceDeviceId != ScaleDeviceConfig.DEVICE_ID_18)
|
||||||
|
ScaleDeviceConfig.SCALE_ORDER_22.indexOf(address) else -1
|
||||||
|
val pos18 = if (sourceDeviceId != ScaleDeviceConfig.DEVICE_ID_22)
|
||||||
|
ScaleDeviceConfig.SCALE_ORDER_18.indexOf(address) else -1
|
||||||
|
|
||||||
|
Log.d(TAG, "triggerHighlight: sourceDeviceId=$sourceDeviceId address=$address pos22=$pos22 pos18=$pos18")
|
||||||
|
|
||||||
|
// 两个列表都不包含该地址,忽略
|
||||||
|
if (pos22 < 0 && pos18 < 0) {
|
||||||
|
Log.d(TAG, "triggerHighlight: address=$address 不在任何 SCALE_ORDER 中,忽略")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mainHandler.post {
|
||||||
|
clearHighlightRunnable?.let { mainHandler.removeCallbacks(it) }
|
||||||
|
|
||||||
|
when {
|
||||||
|
pos22 >= 0 -> {
|
||||||
|
adapter22.highlightPosition(pos22)
|
||||||
|
adapter18.clearHighlight()
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
adapter18.highlightPosition(pos18)
|
||||||
|
adapter22.clearHighlight()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearHighlightRunnable = Runnable {
|
||||||
|
adapter22.clearHighlight()
|
||||||
|
adapter18.clearHighlight()
|
||||||
|
}.also { mainHandler.postDelayed(it, HIGHLIGHT_DURATION_MS) }
|
||||||
|
|
||||||
|
// 子设备:通知主设备(携带本机 deviceId 和地址)
|
||||||
|
if (GlobalData.deviceRole == DeviceRole.SLAVE) {
|
||||||
|
ScaleServiceManager.broadcastEvent(
|
||||||
|
ScaleEvent(
|
||||||
|
type = ScaleEvent.TYPE_SEASONING_ADDED,
|
||||||
|
deviceId = GlobalData.deviceId,
|
||||||
|
address = address,
|
||||||
|
delta = delta
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
// 子设备才注册了 WeightUtil 监听,移除时安全无副作用
|
||||||
|
WeightUtil.removeWeightListener(TAG)
|
||||||
|
ScaleServiceManager.onScaleEvent = null
|
||||||
|
clearHighlightRunnable?.let { mainHandler.removeCallbacks(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="120dp"
|
android:layout_height="120dp"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:paddingEnd="16dp"
|
android:paddingEnd="16dp"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:paddingBottom="8dp">
|
android:paddingBottom="8dp"
|
||||||
|
android:background="@drawable/shape_scale_cell">
|
||||||
|
|
||||||
<!-- 秤编号 -->
|
<!-- 秤编号 -->
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -29,5 +29,6 @@
|
|||||||
<color name="black333">#333333</color>
|
<color name="black333">#333333</color>
|
||||||
<color name="gray_c8">#C8C8C8</color>
|
<color name="gray_c8">#C8C8C8</color>
|
||||||
<color name="gray_e6">#E6E6E6</color>
|
<color name="gray_e6">#E6E6E6</color>
|
||||||
|
<color name="red_ff4444">#FF4444</color>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user