From a81e914b63619130717279c4c45f88d0f929b396 Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Tue, 21 Apr 2026 11:59:59 +0800 Subject: [PATCH] =?UTF-8?q?refactor(adapter):=20=E5=90=88=E5=B9=B6=20Scale?= =?UTF-8?q?18/22GridAdapter=20=E4=B8=BA=20Seasoning18/22GridAdapter?= =?UTF-8?q?=EF=BC=8C=E7=BB=9F=E4=B8=80=E6=94=AF=E6=8C=81=E8=B0=83=E6=96=99?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=92=8C=E7=A7=A4=E6=80=BB=E8=A7=88=E5=8F=8C?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ScaleData 新增可空 name 字段,用于承载调料名称 - Seasoning18/22GridAdapter 改用 ScaleData,新增 showNameOnly 参数区分页面模式 - showNameOnly=true(调料配置):仅显示名称,无名称显示"-" - showNameOnly=false(秤总览):显示名称+重量+状态,无名称显示秤地址编号 - update() 恢复差量更新逻辑,避免 submitList 动画期间点击失效 - MasterScaleActivity、SlaveActivity 改用新 Adapter,删除旧 Scale18/22GridAdapter - 删除 Scale22GridActivity 及其布局和 Manifest 注册 - list_item_scale22_row.xml 中 tvAddr 重命名为 tvName Co-Authored-By: Claude Sonnet 4.6 --- app/src/main/AndroidManifest.xml | 5 - .../dish/match/adapter/Scale18GridAdapter.kt | 139 ----------------- .../dish/match/adapter/Scale22GridAdapter.kt | 141 ----------------- .../match/adapter/Seasoning18GridAdapter.kt | 121 ++++++++++----- .../match/adapter/Seasoning22GridAdapter.kt | 125 ++++++++++----- .../com/shuwei/dish/match/scale/ScaleData.kt | 3 +- .../dish/match/ui/MasterScaleActivity.kt | 16 +- .../dish/match/ui/Scale22GridActivity.kt | 142 ------------------ .../com/shuwei/dish/match/ui/SlaveActivity.kt | 16 +- .../ui/fragment/SeasoningConfigFragment.kt | 4 +- .../main/res/layout/activity_scale22_grid.xml | 46 ------ .../main/res/layout/list_item_scale22_row.xml | 2 +- 12 files changed, 197 insertions(+), 563 deletions(-) delete mode 100644 app/src/main/java/com/shuwei/dish/match/adapter/Scale18GridAdapter.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/adapter/Scale22GridAdapter.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/ui/Scale22GridActivity.kt delete mode 100644 app/src/main/res/layout/activity_scale22_grid.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 330cdcc..88408ac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -106,11 +106,6 @@ android:theme="@style/Theme.DishMatch.NoSplash" android:screenOrientation="portrait" tools:ignore="DiscouragedApi,LockedOrientationActivity" /> - (mutableListOf()) { - - inner class VH(val b: ListItemScale22RowBinding) : QuickViewHolder(b.root) - - /** 用 address=0 的哨兵表示空位 */ - private val empty = ScaleData("", 0, 0.0, -1, 0) - - /** 当前高亮的格子位置,-1 表示无高亮 */ - private var highlightedPosition: Int = -1 - - /** 点击回调,在 onCreateViewHolder 中绑定,不受 notifyItemChanged 影响 */ - var onItemClick: ((ScaleData) -> Unit)? = null - - /** - * 按 SCALE_ORDER_18 顺序更新数据,空位用哨兵填充 - * 只更新显示字段有变化的格子,避免全量刷新 - */ - fun update(newList: List) { - val filled = newList.map { it ?: empty } - if (items.isEmpty()) { - submitList(filled) - } else { - filled.forEachIndexed { i, scale -> - val cur = items.getOrNull(i) - if (cur == null || cur.address != scale.address || cur.weight != scale.weight || cur.state != scale.state) { - set(i, scale) - } - } - } - } - - /** - * 将 ScaleData 列表按 SCALE_ORDER_18 重排后调用 update - */ - fun updateByAddress(scales: List) { - val byAddress = scales.associateBy { it.address } - update(ScaleDeviceConfig.SCALE_ORDER_18.map { byAddress[it] }) - } - - /** 前6个位置为第1行(viewType=0,margin大),其余为第2-3行(viewType=1,margin小) */ - override fun getItemViewType(position: Int, list: List) = if (position < 6) 0 else 1 - - override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { - val b = ListItemScale22RowBinding.inflate(LayoutInflater.from(context), parent, false) - // 第1行 margin=12dp(格子视觉更小),第2-3行 margin=2dp - val margin = if (viewType == 0) 12.dp else 2.dp - val screenWidth = parent.resources.displayMetrics.widthPixels - val cellSize = (screenWidth - 6 * margin * 2 - 10.dp) / 6 - b.root.layoutParams = (b.root.layoutParams as ViewGroup.MarginLayoutParams).also { - it.width = cellSize - it.height = cellSize - // 第1行:顶部间距为0,底部间距2dp,左右保持原有 margin - if (viewType == 0) it.setMargins(margin, 0, margin, 2.dp) - else it.setMargins(margin, margin, margin, margin) - } - val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt() - b.tvAddr.textSize = (baseSp * 0.16f).coerceIn(9f, 14f) - b.tvWeight.textSize = (baseSp * 0.22f).coerceIn(12f, 20f) - b.tvState.textSize = (baseSp * 0.14f).coerceIn(8f, 12f) - val vh = VH(b) - // 在 ViewHolder 创建时绑定点击,只执行一次,不受 notifyItemChanged 重绑定影响 - b.root.setOnClickListener { - val pos = vh.bindingAdapterPosition - if (pos != RecyclerView.NO_POSITION) { - items.getOrNull(pos)?.takeIf { it.address != 0 }?.let { onItemClick?.invoke(it) } - } - } - return vh - } - - @SuppressLint("SetTextI18n") - override fun onBindViewHolder(holder: VH, position: Int, item: ScaleData?) { - val valid = (item?.address ?: 0) != 0 - holder.b.tvAddr.text = if (valid) "秤${item!!.address}" else "" - holder.b.tvWeight.text = if (valid) "${item!!.weight}g" 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) { - WeightUtil.STATE_STABLE -> "稳定" - WeightUtil.STATE_UNSTABLE -> "不稳定" - WeightUtil.STATE_OVER_WEIGHT -> "超量" - else -> "$state" - } -} diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/Scale22GridAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/Scale22GridAdapter.kt deleted file mode 100644 index 9b9e550..0000000 --- a/app/src/main/java/com/shuwei/dish/match/adapter/Scale22GridAdapter.kt +++ /dev/null @@ -1,141 +0,0 @@ -package com.shuwei.dish.match.adapter - -import android.annotation.SuppressLint -import android.content.Context -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.core.content.ContextCompat -import androidx.recyclerview.widget.RecyclerView -import com.chad.library.adapter4.BaseQuickAdapter -import com.chad.library.adapter4.viewholder.QuickViewHolder -import com.google.android.flexbox.FlexboxLayoutManager -import com.shuwei.dish.match.R -import com.shuwei.dish.match.databinding.ListItemScale22RowBinding -import com.shuwei.dish.match.scale.ScaleData -import com.shuwei.dish.match.scale.ScaleDeviceConfig -import com.shuwei.dish.match.utils.WeightUtil -import com.shuwei.dish.match.utils.ext.dp - -/** - * 22个秤的网格 Adapter - * 使用 FlexboxLayoutManager 按列排列,前4个格子较大(对应物理上的大秤),其余较小 - * 点击格子触发清零回调 - * - * @param largeSize 前4个大格子的尺寸(px) - * @param smallSize 其余小格子的尺寸(px) - */ -class Scale22GridAdapter( - private val largeSize: Int, - private val smallSize: Int -) : BaseQuickAdapter(mutableListOf()) { - - inner class VH(val b: ListItemScale22RowBinding) : QuickViewHolder(b.root) - - /** 用 address=0 的哨兵表示空位 */ - private val empty = ScaleData("", 0, 0.0, -1, 0) - - /** 当前高亮的格子位置,-1 表示无高亮 */ - private var highlightedPosition: Int = -1 - - /** 点击回调,在 onCreateViewHolder 中绑定,不受 notifyItemChanged 影响 */ - var onItemClick: ((ScaleData) -> Unit)? = null - - /** - * 按 SCALE_ORDER_22 顺序更新数据,空位用哨兵填充 - * 只更新显示字段有变化的格子,避免全量刷新 - */ - fun update(newList: List) { - val filled = newList.map { it ?: empty } - if (items.isEmpty()) { - submitList(filled) - } else { - filled.forEachIndexed { i, scale -> - val cur = items.getOrNull(i) - if (cur == null || cur.address != scale.address || cur.weight != scale.weight || cur.state != scale.state) { - set(i, scale) - } - } - } - } - - /** - * 将 ScaleData 列表按 SCALE_ORDER_22 重排后调用 update - */ - fun updateByAddress(scales: List) { - val byAddress = scales.associateBy { it.address } - update(ScaleDeviceConfig.SCALE_ORDER_22.map { byAddress[it] }) - } - - /** 前4个位置为大格子(viewType=1),其余为小格子(viewType=0) */ - override fun getItemViewType(position: Int, list: List) = if (position < 4) 1 else 0 - - override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { - val b = ListItemScale22RowBinding.inflate(LayoutInflater.from(context), parent, false) - val cellSize = if (viewType == 1) largeSize else smallSize - (b.root.layoutParams as? FlexboxLayoutManager.LayoutParams)?.apply { - width = cellSize - height = cellSize - setMargins(2.dp, 2.dp, 2.dp, 2.dp) - } - val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt() - b.tvAddr.textSize = (baseSp * 0.16f).coerceIn(9f, 14f) - b.tvWeight.textSize = (baseSp * 0.22f).coerceIn(12f, 20f) - b.tvState.textSize = (baseSp * 0.14f).coerceIn(8f, 12f) - val vh = VH(b) - // 在 ViewHolder 创建时绑定点击,只执行一次,不受 notifyItemChanged 重绑定影响 - b.root.setOnClickListener { - val pos = vh.bindingAdapterPosition - if (pos != RecyclerView.NO_POSITION) { - items.getOrNull(pos)?.takeIf { it.address != 0 }?.let { onItemClick?.invoke(it) } - } - } - return vh - } - - @SuppressLint("SetTextI18n") - override fun onBindViewHolder(holder: VH, position: Int, item: ScaleData?) { - val valid = (item?.address ?: 0) != 0 - holder.b.tvAddr.text = if (valid) "秤${item!!.address}" else "" - holder.b.tvWeight.text = if (valid) "${item!!.weight}g" 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) { - WeightUtil.STATE_STABLE -> "稳定" - WeightUtil.STATE_UNSTABLE -> "不稳定" - WeightUtil.STATE_OVER_WEIGHT -> "超量" - else -> "$state" - } -} diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/Seasoning18GridAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/Seasoning18GridAdapter.kt index f7d94cb..7512cc9 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/Seasoning18GridAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/Seasoning18GridAdapter.kt @@ -1,80 +1,110 @@ package com.shuwei.dish.match.adapter +import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.chad.library.adapter4.BaseQuickAdapter 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.entity.SeasoningEntity +import com.shuwei.dish.match.databinding.ListItemScale22RowBinding +import com.shuwei.dish.match.scale.ScaleData import com.shuwei.dish.match.scale.ScaleDeviceConfig +import com.shuwei.dish.match.utils.WeightUtil import com.shuwei.dish.match.utils.ext.dp /** - * 调料配置-下层(18格)网格 Adapter - * 使用 GridLayoutManager(6列)排列,第1行格子 margin 较大,第2-3行 margin 较小 - * 实体类为 SeasoningEntity,格子显示调料名称 goodsName + * 18格网格 Adapter,同时服务于调料配置页和主设备秤总览页。 + * + * @param showNameOnly true = 调料配置模式,仅显示 tvName,隐藏 tvWeight/tvState; + * false = 秤总览模式,三行全部显示 */ -class Seasoning18GridAdapter : BaseQuickAdapter(mutableListOf()) { +class Seasoning18GridAdapter( + private val showNameOnly: Boolean = false +) : BaseQuickAdapter(mutableListOf()) { - /** 空位哨兵,sort=-1 表示无效 */ - private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 } + /** 空位哨兵,address=0 表示无效 */ + private val empty = ScaleData("", 0, 0.0, -1, 0) /** 当前高亮的格子位置,-1 表示无高亮 */ private var highlightedPosition: Int = -1 - /** 点击回调,预留,暂不使用 */ - var onItemClick: ((SeasoningEntity, Int) -> Unit)? = null + /** 点击回调 */ + var onItemClick: ((ScaleData, Int) -> Unit)? = null - inner class VH(val b: ListItemSeasoningCellBinding) : QuickViewHolder(b.root) + inner class VH(val b: ListItemScale22RowBinding) : QuickViewHolder(b.root) /** 前6个位置为第1行(viewType=0,margin大),其余为第2-3行(viewType=1,margin小) */ - override fun getItemViewType(position: Int, list: List) = + override fun getItemViewType(position: Int, list: List) = if (position < 6) 0 else 1 override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { - val b = ListItemSeasoningCellBinding.inflate(LayoutInflater.from(context), parent, false) + val b = ListItemScale22RowBinding.inflate(LayoutInflater.from(context), parent, false) val margin = if (viewType == 0) 12.dp else 2.dp val screenWidth = parent.resources.displayMetrics.widthPixels val cellSize = (screenWidth - 6 * margin * 2 - 10.dp) / 6 (b.root.layoutParams as? ViewGroup.MarginLayoutParams)?.also { it.width = cellSize it.height = cellSize - // 第1行:顶部间距为0,底部间距2dp,左右保持原有 margin if (viewType == 0) it.setMargins(margin, 0, margin, 2.dp) else it.setMargins(margin, margin, margin, margin) } val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt() - b.tvGoodsName.textSize = (baseSp * 0.18f).coerceIn(10f, 16f) + b.tvName.textSize = (baseSp * 0.18f).coerceIn(10f, 16f) + b.tvWeight.textSize = (baseSp * 0.22f).coerceIn(12f, 20f) + b.tvState.textSize = (baseSp * 0.14f).coerceIn(8f, 12f) + // 调料配置模式隐藏重量和状态行 + if (showNameOnly) { + b.tvWeight.visibility = View.GONE + b.tvState.visibility = View.GONE + } val vh = VH(b) - // 点击事件预留,暂不处理 b.root.setOnClickListener { val pos = vh.bindingAdapterPosition if (pos != RecyclerView.NO_POSITION) { - items.getOrNull(pos)?.let { item -> onItemClick?.invoke(item, pos) } + items.getOrNull(pos)?.takeIf { it.address != 0 }?.let { item -> onItemClick?.invoke(item, pos) } } } return vh } - override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) { - holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-" + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: VH, position: Int, item: ScaleData?) { + val valid = (item?.address ?: 0) != 0 + if (!valid) { + holder.b.tvName.text = if (showNameOnly) "-" else "" + holder.b.tvWeight.text = "" + holder.b.tvState.text = "" + } else { + holder.b.tvName.text = when { + !item!!.name.isNullOrBlank() -> item.name + showNameOnly -> "-" + else -> "秤${item.address}" + } + holder.b.tvWeight.text = "${item!!.weight}g" + holder.b.tvState.text = stateStr(item.state) + } // 高亮格子:红底白字;其余恢复默认 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)) + 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.tvName.setTextColor(white) + holder.b.tvWeight.setTextColor(white) + holder.b.tvState.setTextColor(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)) + holder.b.root.setBackgroundResource(R.drawable.shape_scale_cell) + val normal = ContextCompat.getColor(holder.b.root.context, R.color.home_title) + holder.b.tvName.setTextColor(normal) + holder.b.tvWeight.setTextColor(normal) + holder.b.tvState.setTextColor(normal) } } /** * 高亮指定位置的格子(红底白字),同时清除上一个高亮 - * @param position 要高亮的格子索引 */ fun highlightPosition(position: Int) { val prev = highlightedPosition @@ -91,23 +121,46 @@ class Seasoning18GridAdapter : BaseQuickAdapter) { - val bySort = seasoningList.associateBy { it.sort } - val filled = ScaleDeviceConfig.SCALE_ORDER_18.map { sort -> - bySort[sort] ?: empty.copy().also { it.sort = sort } + fun update(list: List) { + val byAddress = list.associateBy { it.address } + val filled = ScaleDeviceConfig.SCALE_ORDER_18.map { addr -> + byAddress[addr] ?: empty.copy(address = addr) } - submitList(filled) + if (items.isEmpty()) { + submitList(filled) + } else { + filled.forEachIndexed { i, scale -> + val cur = items.getOrNull(i) + if (cur == null || cur.address != scale.address || cur.weight != scale.weight || cur.state != scale.state || cur.name != scale.name) { + set(i, scale) + } + } + } + } + + /** + * 将 ScaleData 列表按 SCALE_ORDER_18 重排后调用 update + */ + fun updateByAddress(scales: List) { + update(scales) + } + + private fun stateStr(state: Int) = when (state) { + WeightUtil.STATE_STABLE -> "稳定" + WeightUtil.STATE_UNSTABLE -> "不稳定" + WeightUtil.STATE_OVER_WEIGHT -> "超量" + else -> "" } } diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/Seasoning22GridAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/Seasoning22GridAdapter.kt index a089296..51136b4 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/Seasoning22GridAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/Seasoning22GridAdapter.kt @@ -1,7 +1,9 @@ package com.shuwei.dish.match.adapter +import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView @@ -9,41 +11,43 @@ import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.google.android.flexbox.FlexboxLayoutManager import com.shuwei.dish.match.R -import com.shuwei.dish.match.databinding.ListItemSeasoningCellBinding -import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.databinding.ListItemScale22RowBinding +import com.shuwei.dish.match.scale.ScaleData import com.shuwei.dish.match.scale.ScaleDeviceConfig +import com.shuwei.dish.match.utils.WeightUtil import com.shuwei.dish.match.utils.ext.dp /** - * 调料配置-上层(22格)网格 Adapter - * 使用 FlexboxLayoutManager 按列排列,前4个格子较大,其余较小 - * 实体类为 SeasoningEntity,格子显示调料名称 goodsName + * 22格网格 Adapter,同时服务于调料配置页和主设备秤总览页。 * - * @param largeSize 前4个大格子的尺寸(px) - * @param smallSize 其余小格子的尺寸(px) + * @param largeSize 前4个大格子的尺寸(px) + * @param smallSize 其余小格子的尺寸(px) + * @param showNameOnly true = 调料配置模式,仅显示 tvName,隐藏 tvWeight/tvState; + * false = 秤总览模式,三行全部显示 */ class Seasoning22GridAdapter( private val largeSize: Int, - private val smallSize: Int -) : BaseQuickAdapter(mutableListOf()) { + private val smallSize: Int, + private val showNameOnly: Boolean = false +) : BaseQuickAdapter(mutableListOf()) { - /** 空位哨兵,sort=-1 表示无效 */ - private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 } + /** 空位哨兵,address=0 表示无效 */ + private val empty = ScaleData("", 0, 0.0, -1, 0) /** 当前高亮的格子位置,-1 表示无高亮 */ private var highlightedPosition: Int = -1 - /** 点击回调,预留,暂不使用 */ - var onItemClick: ((SeasoningEntity, Int) -> Unit)? = null + /** 点击回调 */ + var onItemClick: ((ScaleData, Int) -> Unit)? = null - inner class VH(val b: ListItemSeasoningCellBinding) : QuickViewHolder(b.root) + inner class VH(val b: ListItemScale22RowBinding) : QuickViewHolder(b.root) /** 前4个位置为大格子(viewType=1),其余为小格子(viewType=0) */ - override fun getItemViewType(position: Int, list: List) = + override fun getItemViewType(position: Int, list: List) = if (position < 4) 1 else 0 override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { - val b = ListItemSeasoningCellBinding.inflate(LayoutInflater.from(context), parent, false) + val b = ListItemScale22RowBinding.inflate(LayoutInflater.from(context), parent, false) val cellSize = if (viewType == 1) largeSize else smallSize (b.root.layoutParams as? FlexboxLayoutManager.LayoutParams)?.apply { width = cellSize @@ -51,33 +55,59 @@ class Seasoning22GridAdapter( setMargins(2.dp, 2.dp, 2.dp, 2.dp) } val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt() - b.tvGoodsName.textSize = (baseSp * 0.18f).coerceIn(10f, 16f) + b.tvName.textSize = (baseSp * 0.18f).coerceIn(10f, 16f) + b.tvWeight.textSize = (baseSp * 0.22f).coerceIn(12f, 20f) + b.tvState.textSize = (baseSp * 0.14f).coerceIn(8f, 12f) + // 调料配置模式隐藏重量和状态行 + if (showNameOnly) { + b.tvWeight.visibility = View.GONE + b.tvState.visibility = View.GONE + } val vh = VH(b) - // 点击事件预留,暂不处理 b.root.setOnClickListener { val pos = vh.bindingAdapterPosition if (pos != RecyclerView.NO_POSITION) { - items.getOrNull(pos)?.let { item -> onItemClick?.invoke(item, pos) } + items.getOrNull(pos)?.takeIf { it.address != 0 }?.let { item -> onItemClick?.invoke(item, pos) } } } return vh } - override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) { - holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-" + @SuppressLint("SetTextI18n") + override fun onBindViewHolder(holder: VH, position: Int, item: ScaleData?) { + val valid = (item?.address ?: 0) != 0 + if (!valid) { + holder.b.tvName.text = if (showNameOnly) "-" else "" + holder.b.tvWeight.text = "" + holder.b.tvState.text = "" + } else { + // 有名称显示名称,无名称:调料配置显示"-",秤总览显示秤地址编号 + holder.b.tvName.text = when { + !item!!.name.isNullOrBlank() -> item.name + showNameOnly -> "-" + else -> "秤${item.address}" + } + holder.b.tvWeight.text = "${item!!.weight}g" + holder.b.tvState.text = stateStr(item.state) + } // 高亮格子:红底白字;其余恢复默认 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)) + 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.tvName.setTextColor(white) + holder.b.tvWeight.setTextColor(white) + holder.b.tvState.setTextColor(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)) + holder.b.root.setBackgroundResource(R.drawable.shape_scale_cell) + val normal = ContextCompat.getColor(holder.b.root.context, R.color.home_title) + holder.b.tvName.setTextColor(normal) + holder.b.tvWeight.setTextColor(normal) + holder.b.tvState.setTextColor(normal) } } /** * 高亮指定位置的格子(红底白字),同时清除上一个高亮 - * @param position 要高亮的格子索引 */ fun highlightPosition(position: Int) { val prev = highlightedPosition @@ -94,23 +124,46 @@ class Seasoning22GridAdapter( } /** - * 更新指定位置的调料名称并刷新对应格子 - * @param position 格子索引 - * @param name 新的调料名称 + * 更新指定位置的名称并刷新对应格子 */ fun updateItemName(position: Int, name: String) { - items.getOrNull(position)?.goodsName = name - notifyItemChanged(position) + items.getOrNull(position)?.let { + set(position, it.copy(name = name)) + } } /** * 按 SCALE_ORDER_22 的 sort 顺序更新数据,空位用哨兵填充 + * 只更新显示字段有变化的格子,避免全量刷新导致 ViewHolder 重建影响点击监听 */ - fun update(seasoningList: List) { - val bySort = seasoningList.associateBy { it.sort } - val filled = ScaleDeviceConfig.SCALE_ORDER_22.map { sort -> - bySort[sort] ?: empty.copy().also { it.sort = sort } + fun update(list: List) { + val byAddress = list.associateBy { it.address } + val filled = ScaleDeviceConfig.SCALE_ORDER_22.map { addr -> + byAddress[addr] ?: empty.copy(address = addr) } - submitList(filled) + if (items.isEmpty()) { + submitList(filled) + } else { + filled.forEachIndexed { i, scale -> + val cur = items.getOrNull(i) + if (cur == null || cur.address != scale.address || cur.weight != scale.weight || cur.state != scale.state || cur.name != scale.name) { + set(i, scale) + } + } + } + } + + /** + * 将 ScaleData 列表按 SCALE_ORDER_22 重排后调用 update + */ + fun updateByAddress(scales: List) { + update(scales) + } + + private fun stateStr(state: Int) = when (state) { + WeightUtil.STATE_STABLE -> "稳定" + WeightUtil.STATE_UNSTABLE -> "不稳定" + WeightUtil.STATE_OVER_WEIGHT -> "超量" + else -> "" } } diff --git a/app/src/main/java/com/shuwei/dish/match/scale/ScaleData.kt b/app/src/main/java/com/shuwei/dish/match/scale/ScaleData.kt index 384441e..2fa647c 100644 --- a/app/src/main/java/com/shuwei/dish/match/scale/ScaleData.kt +++ b/app/src/main/java/com/shuwei/dish/match/scale/ScaleData.kt @@ -15,5 +15,6 @@ data class ScaleData( val weight: Double, val state: Int, val ts: Long, - val ip: String = "" + val ip: String = "", + val name: String? = null ) diff --git a/app/src/main/java/com/shuwei/dish/match/ui/MasterScaleActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/MasterScaleActivity.kt index 35213b1..b9af322 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/MasterScaleActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/MasterScaleActivity.kt @@ -18,8 +18,8 @@ import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexWrap import com.google.android.flexbox.FlexboxLayoutManager import com.shuwei.dish.match.R -import com.shuwei.dish.match.adapter.Scale18GridAdapter -import com.shuwei.dish.match.adapter.Scale22GridAdapter +import com.shuwei.dish.match.adapter.Seasoning18GridAdapter +import com.shuwei.dish.match.adapter.Seasoning22GridAdapter import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.dialog.CommonDialog import com.shuwei.dish.match.databinding.ActivityMasterScaleBinding @@ -249,7 +249,7 @@ class MasterScaleActivity : BaseActivity() { // ── ViewHolder: 22个秤 ───────────────────────────────────────────────── private inner class Scale22VH(val b: ListTypeScale22Binding) : RecyclerView.ViewHolder(b.root) { - private var innerAdapter: Scale22GridAdapter? = null + private var innerAdapter: Seasoning22GridAdapter? = null @SuppressLint("SetTextI18n") fun bind(group: DeviceGroup) { @@ -269,8 +269,8 @@ class MasterScaleActivity : BaseActivity() { alignItems = AlignItems.FLEX_START } b.rvScales.itemAnimator = null - innerAdapter = Scale22GridAdapter(largeSize, smallSize).apply { - onItemClick = { scale -> + innerAdapter = Seasoning22GridAdapter(largeSize, smallSize, showNameOnly = false).apply { + onItemClick = { scale, _ -> showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address) } } @@ -288,7 +288,7 @@ class MasterScaleActivity : BaseActivity() { // ── ViewHolder: 18个秤 ───────────────────────────────────────────────── private inner class Scale18VH(val b: ListTypeScale18Binding) : RecyclerView.ViewHolder(b.root) { - private var innerAdapter: Scale18GridAdapter? = null + private var innerAdapter: Seasoning18GridAdapter? = null @SuppressLint("SetTextI18n") fun bind(group: DeviceGroup) { @@ -303,8 +303,8 @@ class MasterScaleActivity : BaseActivity() { } b.rvScales.layoutManager = gridLayoutManager b.rvScales.itemAnimator = null - innerAdapter = Scale18GridAdapter().apply { - onItemClick = { scale -> + innerAdapter = Seasoning18GridAdapter(showNameOnly = false).apply { + onItemClick = { scale, _ -> showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address) } } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/Scale22GridActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/Scale22GridActivity.kt deleted file mode 100644 index 12e5778..0000000 --- a/app/src/main/java/com/shuwei/dish/match/ui/Scale22GridActivity.kt +++ /dev/null @@ -1,142 +0,0 @@ -package com.shuwei.dish.match.ui - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.RecyclerView -import com.google.android.flexbox.AlignItems -import com.google.android.flexbox.FlexDirection -import com.google.android.flexbox.FlexWrap -import com.google.android.flexbox.FlexboxLayoutManager -import com.shuwei.dish.match.base.BaseActivity -import com.shuwei.dish.match.databinding.ActivityScale22GridBinding -import com.shuwei.dish.match.databinding.ListItemScale22RowBinding -import com.shuwei.dish.match.scale.ScaleData -import com.shuwei.dish.match.scale.ScaleServiceManager -import com.shuwei.dish.match.utils.WeightUtil -import com.shuwei.dish.match.utils.ext.dp -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch - -/** 22格设备的 deviceId */ -private const val DEVICE_ID_22 = "a46fa55c-113c-3511-bb1f-41e5eff77c4b" - -/** - * 列优先顺序:大列1(20,22) → 大列2(19,21) → 小列1~6 - * FlexboxLayoutManager COLUMN 方向按列填充,顺序天然对应 - */ -private val SCALE_ORDER_22 = listOf(20, 22, 19, 21, 6, 12, 18, 5, 11, 17, 4, 10, 16, 3, 9, 15, 2, 8, 14, 1, 7, 13) - -/** 前4个 item(大列1+大列2,各2行)为大格子 */ -private fun isLargeCell(position: Int) = position < 4 - -/** - * 22格秤混合网格布局页 - * FlexboxLayoutManager(COLUMN) 竖向排列: - * - 大格子 flexBasisPercent=1.5/9,每列2个,自动换列 - * - 小格子 flexBasisPercent=1.0/9,每列3个,自动换列 - */ -class Scale22GridActivity : BaseActivity() { - - private lateinit var binding: ActivityScale22GridBinding - private val scaleList = mutableListOf() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityScale22GridBinding.inflate(layoutInflater) - setContentView(binding.root) - setHeaderBackground() - - binding.ivBack.setOnClickListener { finish() } - - // 格子尺寸计算: - // 屏幕宽 - 8列×左右margin(2dp×2) = 可用宽,共9份(大1.5+小1.0×6) - val screenWidth = resources.displayMetrics.widthPixels - val usable = screenWidth - 8 * 4.dp - 4.dp // 减去8列格子margin + RecyclerView左右margin各2dp - val smallSize = usable / 9 - val largeSize = (usable - smallSize * 6) / 2 // 剩余宽度平分给2列大格子 - // 高度以小格子3行为准(3行×格子高 + 3行×上下margin各2dp) - val rvHeight = smallSize * 3 + 3 * 4.dp - binding.rvScale22.layoutParams = binding.rvScale22.layoutParams.also { - it.height = rvHeight - } - - binding.rvScale22.layoutManager = FlexboxLayoutManager(this).apply { - flexDirection = FlexDirection.COLUMN - flexWrap = FlexWrap.WRAP - alignItems = AlignItems.FLEX_START - } - binding.rvScale22.itemAnimator = null - binding.rvScale22.adapter = Scale22Adapter(scaleList, largeSize, smallSize) - - observeScaleData() - } - - private fun observeScaleData() { - val flow = ScaleServiceManager.allScales ?: return - lifecycleScope.launch { - flow.collectLatest { scaleMap -> - val scaleByAddress = scaleMap.values - .filter { it.deviceId == DEVICE_ID_22 } - .associateBy { it.address } - val newList = SCALE_ORDER_22.map { scaleByAddress[it] } - if (scaleList.isEmpty()) { - scaleList.addAll(newList) - binding.rvScale22.adapter?.notifyItemRangeInserted(0, scaleList.size) - } else { - newList.forEachIndexed { i, scale -> - if (scaleList[i] != scale) { - scaleList[i] = scale - binding.rvScale22.adapter?.notifyItemChanged(i) - } - } - } - } - } - } - - private inner class Scale22Adapter( - private val data: List, - private val largeSize: Int, // 大格子宽高(px) - private val smallSize: Int // 小格子宽高(px) - ) : RecyclerView.Adapter() { - - inner class VH(val b: ListItemScale22RowBinding) : RecyclerView.ViewHolder(b.root) - - override fun getItemViewType(position: Int) = if (isLargeCell(position)) 1 else 0 - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { - val b = ListItemScale22RowBinding.inflate(LayoutInflater.from(parent.context), parent, false) - val cellSize = if (viewType == 1) largeSize else smallSize - // 固定宽高实现正方形,FlexboxLayoutManager COLUMN 方向按高度装满后自动换列 - (b.root.layoutParams as? FlexboxLayoutManager.LayoutParams)?.apply { - width = cellSize - height = cellSize - setMargins(2.dp, 2.dp, 2.dp, 2.dp) - } - val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt() - b.tvAddr.textSize = (baseSp * 0.16f).coerceIn(9f, 14f) - b.tvWeight.textSize = (baseSp * 0.22f).coerceIn(12f, 20f) - b.tvState.textSize = (baseSp * 0.14f).coerceIn(8f, 12f) - return VH(b) - } - - override fun getItemCount() = data.size - - @SuppressLint("SetTextI18n") - override fun onBindViewHolder(holder: VH, position: Int) { - val scale = data[position] - holder.b.tvAddr.text = scale?.let { "秤${it.address}" } ?: "" - holder.b.tvWeight.text = scale?.let { "${it.weight}g" } ?: "" - holder.b.tvState.text = when (scale?.state) { - WeightUtil.STATE_STABLE -> "稳定" - WeightUtil.STATE_UNSTABLE -> "不稳定" - WeightUtil.STATE_OVER_WEIGHT -> "超量" - null -> "" - else -> "${scale.state}" - } - } - } -} diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SlaveActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SlaveActivity.kt index 0013c09..8a58657 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/SlaveActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/SlaveActivity.kt @@ -13,8 +13,8 @@ import com.google.android.flexbox.AlignItems import com.google.android.flexbox.FlexDirection import com.google.android.flexbox.FlexWrap import com.google.android.flexbox.FlexboxLayoutManager -import com.shuwei.dish.match.adapter.Scale18GridAdapter -import com.shuwei.dish.match.adapter.Scale22GridAdapter +import com.shuwei.dish.match.adapter.Seasoning18GridAdapter +import com.shuwei.dish.match.adapter.Seasoning22GridAdapter import com.shuwei.dish.match.adapter.ScaleRowAdapter import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.GlobalData @@ -40,9 +40,9 @@ class SlaveActivity : BaseActivity() { private val linearAdapter = ScaleRowAdapter() /** 22个秤网格 adapter */ - private var scale22Adapter: Scale22GridAdapter? = null + private var scale22Adapter: Seasoning22GridAdapter? = null /** 18个秤网格 adapter */ - private var scale18Adapter: Scale18GridAdapter? = null + private var scale18Adapter: Seasoning18GridAdapter? = null /** 已连接的主设备数量(有连接即代表主设备在线) */ private var masterConnected = false @@ -95,8 +95,8 @@ class SlaveActivity : BaseActivity() { flexWrap = FlexWrap.WRAP alignItems = AlignItems.FLEX_START } - scale22Adapter = Scale22GridAdapter(largeSize, smallSize).also { - it.onItemClick = { scale -> showTareDialog(scale.address) } + scale22Adapter = Seasoning22GridAdapter(largeSize, smallSize, showNameOnly = false).also { + it.onItemClick = { scale, _ -> showTareDialog(scale.address) } } binding.rvScaleList.adapter = scale22Adapter } @@ -107,8 +107,8 @@ class SlaveActivity : BaseActivity() { override fun getSpanSize(position: Int) = 1 } } - scale18Adapter = Scale18GridAdapter().also { - it.onItemClick = { scale -> showTareDialog(scale.address) } + scale18Adapter = Seasoning18GridAdapter(showNameOnly = false).also { + it.onItemClick = { scale, _ -> showTareDialog(scale.address) } } binding.rvScaleList.adapter = scale18Adapter } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/SeasoningConfigFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/SeasoningConfigFragment.kt index f4f2700..cb94fc7 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/fragment/SeasoningConfigFragment.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/SeasoningConfigFragment.kt @@ -45,7 +45,7 @@ class SeasoningConfigFragment : BaseFragment() { // private lateinit var appViewModel: AppViewModel private val adapter22 by lazy { buildAdapter22() } - private val adapter18 by lazy { Seasoning18GridAdapter() } + private val adapter18 by lazy { Seasoning18GridAdapter(showNameOnly = true) } /** 各秤的上一次重量快照,key 格式 "{deviceId}#{address}",用于计算 delta */ private val weightSnapshot = mutableMapOf() @@ -96,7 +96,7 @@ class SeasoningConfigFragment : BaseFragment() { val usable = resources.displayMetrics.widthPixels - 8 * 4.dp - 10.dp val smallSize = usable / 9 val largeSize = (usable - smallSize * 6) / 2 - return Seasoning22GridAdapter(largeSize, smallSize) + return Seasoning22GridAdapter(largeSize, smallSize, showNameOnly = true) } /** 配置两个 RecyclerView */ diff --git a/app/src/main/res/layout/activity_scale22_grid.xml b/app/src/main/res/layout/activity_scale22_grid.xml deleted file mode 100644 index 79662cb..0000000 --- a/app/src/main/res/layout/activity_scale22_grid.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/list_item_scale22_row.xml b/app/src/main/res/layout/list_item_scale22_row.xml index a66ac96..9376d52 100644 --- a/app/src/main/res/layout/list_item_scale22_row.xml +++ b/app/src/main/res/layout/list_item_scale22_row.xml @@ -9,7 +9,7 @@ android:background="@drawable/shape_scale_cell">