refactor(ui): 重构 Scale2VH 使用 XML 布局,优化秤总览界面样式

- list_type_scale2.xml 替换动态格子为 include list_item_scale22_row,Scale2VH 删除 makeScaleCell/updateScaleCell/makeCellText
- list_type_scale1.xml 布局调整为垂直居中,新增 tvName 展示调料名称
- list_type_scale18/22.xml 移除分割线
- SeasoningSelectDialog 搜索列表改为3列,扩充模拟调料数据

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 15:54:04 +08:00
co-authored by Claude Sonnet 4.6
parent b16d7d19cd
commit 3fc54d5f52
6 changed files with 241 additions and 179 deletions
@@ -86,7 +86,7 @@ class SeasoningSelectDialog(
// RecyclerView 初始化及滑动冲突处理
binding.recyclerView.run {
layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false)
adapter = this@SeasoningSelectDialog.adapter
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) {
@@ -134,19 +134,15 @@ class SeasoningSelectDialog(
*/
private fun getGoodsList() {
// TODO: 联调时注释掉下方模拟数据,取消注释真实接口调用
val mockData = mutableListOf(
SeasoningEntity(goodsId = "1", goodsName = "精盐"),
SeasoningEntity(goodsId = "2", goodsName = "鸡精"),
SeasoningEntity(goodsId = "3", goodsName = "陈醋"),
SeasoningEntity(goodsId = "4", goodsName = "老抽"),
SeasoningEntity(goodsId = "5", goodsName = "生抽"),
SeasoningEntity(goodsId = "6", goodsName = "花生油"),
SeasoningEntity(goodsId = "7", goodsName = "菜籽油"),
SeasoningEntity(goodsId = "8", goodsName = "胡椒粉"),
SeasoningEntity(goodsId = "9", goodsName = "十三香"),
SeasoningEntity(goodsId = "10", goodsName = "料酒"),
SeasoningEntity(goodsId = "11", goodsName = "白醋")
val nameList = listOf(
"", "白砂糖","冰糖", "鸡精", "味精", "陈醋", "老抽", "生抽", "番茄酱", "胡椒粉", "孜然", "十三香",
"料酒", "白醋", "蚝油", "辣椒面", "辣椒酱", "豆瓣酱", "淀粉", "", "", "", "香菜"
)
val mockData = mutableListOf<SeasoningEntity>()
nameList.forEachIndexed { index, name ->
val padString = "${index + 1}".padStart(2, '0')
mockData.add(SeasoningEntity(goodsId = "1000$padString", goodsName = name))
}
loadGoodsList(mockData)
// val param = mutableMapOf<String, Any>(
@@ -3,11 +3,9 @@ package com.shuwei.dish.match.ui
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -28,6 +26,7 @@ import com.shuwei.dish.match.databinding.ListTypeScale18Binding
import com.shuwei.dish.match.databinding.ListTypeScale2Binding
import com.shuwei.dish.match.databinding.ListTypeScale22Binding
import com.shuwei.dish.match.base.BaseApp
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.scale.ScaleServiceManager
@@ -108,7 +107,8 @@ class MasterScaleActivity : BaseActivity() {
.groupBy { it.deviceId }
.entries
.sortedWith(compareBy { entry ->
ScaleDeviceConfig.DEVICE_ORDER.indexOf(entry.key).let { if (it == -1) Int.MAX_VALUE else it }
ScaleDeviceConfig.DEVICE_ORDER.indexOf(entry.key)
.let { if (it == -1) Int.MAX_VALUE else it }
})
.map { (deviceId, scales) ->
DeviceGroup(
@@ -125,7 +125,12 @@ class MasterScaleActivity : BaseActivity() {
if (structureChanged) {
updateList(groups)
} else {
groups.forEach { group -> adapter.updateScalesForDevice(group.deviceId, group.scales) }
groups.forEach { group ->
adapter.updateScalesForDevice(
group.deviceId,
group.scales
)
}
}
updateStatusBar(scaleMap)
@@ -180,10 +185,24 @@ class MasterScaleActivity : BaseActivity() {
}
init {
addItemType(VIEW_TYPE_SCALE2, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
addItemType(
VIEW_TYPE_SCALE2,
object :
BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
override fun onCreate(context: Context, parent: ViewGroup, viewType: Int) =
QuickViewHolder(ListTypeScale2Binding.inflate(LayoutInflater.from(context), parent, false).root)
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
QuickViewHolder(
ListTypeScale2Binding.inflate(
LayoutInflater.from(context),
parent,
false
).root
)
override fun onBind(
holder: QuickViewHolder,
position: Int,
item: DeviceGroup?
) {
item ?: return
val b = ListTypeScale2Binding.bind(holder.itemView)
val vh = Scale2VH(b)
@@ -191,10 +210,24 @@ class MasterScaleActivity : BaseActivity() {
vh.bind(item)
}
})
addItemType(VIEW_TYPE_SCALE22, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
addItemType(
VIEW_TYPE_SCALE22,
object :
BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
override fun onCreate(context: Context, parent: ViewGroup, viewType: Int) =
QuickViewHolder(ListTypeScale22Binding.inflate(LayoutInflater.from(context), parent, false).root)
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
QuickViewHolder(
ListTypeScale22Binding.inflate(
LayoutInflater.from(context),
parent,
false
).root
)
override fun onBind(
holder: QuickViewHolder,
position: Int,
item: DeviceGroup?
) {
item ?: return
val b = ListTypeScale22Binding.bind(holder.itemView)
// 每次 onBind 时注册/更新 VH 缓存,后续数据更新直接走 VH,不再触发 notify
@@ -203,10 +236,24 @@ class MasterScaleActivity : BaseActivity() {
vh.bind(item)
}
})
addItemType(VIEW_TYPE_SCALE18, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
addItemType(
VIEW_TYPE_SCALE18,
object :
BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
override fun onCreate(context: Context, parent: ViewGroup, viewType: Int) =
QuickViewHolder(ListTypeScale18Binding.inflate(LayoutInflater.from(context), parent, false).root)
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
QuickViewHolder(
ListTypeScale18Binding.inflate(
LayoutInflater.from(context),
parent,
false
).root
)
override fun onBind(
holder: QuickViewHolder,
position: Int,
item: DeviceGroup?
) {
item ?: return
val b = ListTypeScale18Binding.bind(holder.itemView)
val vh = Scale18VH(b)
@@ -214,10 +261,24 @@ class MasterScaleActivity : BaseActivity() {
vh.bind(item)
}
})
addItemType(VIEW_TYPE_SCALE1, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
addItemType(
VIEW_TYPE_SCALE1,
object :
BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
override fun onCreate(context: Context, parent: ViewGroup, viewType: Int) =
QuickViewHolder(ListTypeScale1Binding.inflate(LayoutInflater.from(context), parent, false).root)
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
QuickViewHolder(
ListTypeScale1Binding.inflate(
LayoutInflater.from(context),
parent,
false
).root
)
override fun onBind(
holder: QuickViewHolder,
position: Int,
item: DeviceGroup?
) {
item ?: return
val b = ListTypeScale1Binding.bind(holder.itemView)
val vh = Scale1VH(b)
@@ -225,49 +286,63 @@ class MasterScaleActivity : BaseActivity() {
vh.bind(item)
}
})
onItemViewType { position, list -> when (list.getOrNull(position)?.deviceId) {
onItemViewType { position, list ->
when (list.getOrNull(position)?.deviceId) {
ScaleDeviceConfig.DEVICE_ID_2 -> VIEW_TYPE_SCALE2
ScaleDeviceConfig.DEVICE_ID_22 -> VIEW_TYPE_SCALE22
ScaleDeviceConfig.DEVICE_ID_18 -> VIEW_TYPE_SCALE18
else -> VIEW_TYPE_SCALE1
}}
}
}
}
}
// ── ViewHolder: 2个秤 ──────────────────────────────────────────────────
private inner class Scale2VH(val b: ListTypeScale2Binding) : RecyclerView.ViewHolder(b.root) {
private lateinit var cells: List<LinearLayout>
@SuppressLint("SetTextI18n")
fun bind(group: DeviceGroup) {
b.tvDeviceIp.text = "IP${group.ip.ifEmpty { "未知" }}"
b.tvDeviceLabel.text = "设备:${group.deviceId}"
if (!::cells.isInitialized) {
val cellSize = (resources.displayMetrics.widthPixels - 4 * 4.dp - 10.dp) / 2
cells = group.scales.take(2).map { scale ->
makeScaleCell(scale, cellSize).also { cell ->
cell.setOnClickListener {
val s = cell.tag as? ScaleData ?: return@setOnClickListener
// 两个格子显示秤地址行,隐藏重量/状态(showAddress 通过 visibility 控制)
listOf(b.cell1, b.cell2).forEach { cell ->
cell.tvAddress.visibility = android.view.View.VISIBLE
cell.tvWeight.visibility = android.view.View.VISIBLE
cell.tvState.visibility = android.view.View.VISIBLE
cell.root.setOnClickListener {
val s = cell.root.tag as? ScaleData ?: return@setOnClickListener
showTareDialog(deviceId = s.deviceId, ip = s.ip, address = s.address)
}
}
}
b.llScaleContainer.removeAllViews()
cells.forEach { b.llScaleContainer.addView(it) }
}
updateScales(group.scales)
}
fun updateScales(scales: List<ScaleData>) {
if (!::cells.isInitialized) return
scales.take(2).forEachIndexed { i, scale ->
cells.getOrNull(i)?.let { cell ->
cell.tag = scale
updateScaleCell(cell, scale)
bindCell(b.cell1, scales.getOrNull(0))
bindCell(b.cell2, scales.getOrNull(1))
}
@SuppressLint("SetTextI18n")
private fun bindCell(cell: ListItemScale22RowBinding, scale: ScaleData?) {
cell.root.tag = scale
cell.tvAddress.run {
text = scale?.let { "${it.address}" } ?: ""
textSize = 18f
}
cell.tvName.run {
text = if (scale?.address == 1) "净材" else "熟食"
textSize = 22f
}
cell.tvWeight.run {
text = scale?.let { "${it.weight}g" } ?: ""
textSize = 22f
}
cell.tvState.run {
text = stateStr(scale?.state)
textSize = 22f
}
cell.root.updateLayoutParams { height = 400.dp }
}
}
@@ -294,9 +369,18 @@ class MasterScaleActivity : BaseActivity() {
alignItems = AlignItems.FLEX_START
}
b.rvScales.itemAnimator = null
innerAdapter = Seasoning22GridAdapter(largeSize, smallSize, showNameOnly = false, showAddress = true).apply {
innerAdapter = Seasoning22GridAdapter(
largeSize,
smallSize,
showNameOnly = false,
showAddress = true
).apply {
onItemClick = { scale, _ ->
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
showTareDialog(
deviceId = scale.deviceId,
ip = scale.ip,
address = scale.address
)
}
}
b.rvScales.adapter = innerAdapter
@@ -328,9 +412,14 @@ class MasterScaleActivity : BaseActivity() {
}
b.rvScales.layoutManager = gridLayoutManager
b.rvScales.itemAnimator = null
innerAdapter = Seasoning18GridAdapter(showNameOnly = false, showAddress = true).apply {
innerAdapter =
Seasoning18GridAdapter(showNameOnly = false, showAddress = true).apply {
onItemClick = { scale, _ ->
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
showTareDialog(
deviceId = scale.deviceId,
ip = scale.ip,
address = scale.address
)
}
}
b.rvScales.adapter = innerAdapter
@@ -381,49 +470,6 @@ class MasterScaleActivity : BaseActivity() {
else -> "$state"
}
/** 创建正方形秤格子(用于 Scale2VH 初始化) */
@SuppressLint("SetTextI18n")
private fun makeScaleCell(scale: ScaleData, cellSize: Int): LinearLayout {
val margin = 2.dp
return LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
layoutParams = LinearLayout.LayoutParams(0, cellSize - margin * 2, 1f)
.also { it.setMargins(margin, margin, margin, margin) }
setPadding(4.dp, 2.dp, 4.dp, 2.dp)
setBackgroundResource(R.drawable.shape_scale_cell)
val baseSize = ((cellSize - margin * 2) / resources.displayMetrics.density).toInt()
addView(makeCellText("${scale.address}", (baseSize * 0.16f).coerceIn(9f, 14f).toInt()))
val foodType = if (scale.address == 1) "净材" else "熟食"
addView(makeCellText(foodType, (baseSize * 0.16f).coerceIn(9f, 14f).toInt()))
addView(makeCellText("${scale.weight}g", (baseSize * 0.22f).coerceIn(12f, 20f).toInt(), bold = true))
addView(makeCellText(stateStr(scale.state), (baseSize * 0.14f).coerceIn(8f, 12f).toInt()))
tag = scale
}
}
/** 仅更新格子内文字,不重建 view(用于 Scale2VH 数据刷新) */
@SuppressLint("SetTextI18n")
private fun updateScaleCell(cell: LinearLayout, scale: ScaleData) {
(cell.getChildAt(0) as? TextView)?.text = "${scale.address}"
(cell.getChildAt(1) as? TextView)?.text = if (scale.address == 1) "净材" else "熟食"
(cell.getChildAt(2) as? TextView)?.text = "${scale.weight}g"
(cell.getChildAt(3) as? TextView)?.text = stateStr(scale.state)
}
private fun makeCellText(text: String, spSize: Int, bold: Boolean = false): TextView =
TextView(this).apply {
this.text = text
textSize = spSize.toFloat()
if (bold) setTypeface(null, android.graphics.Typeface.BOLD)
setTextColor(if (bold) getColor(R.color.home_title) else getColor(R.color.home_sub_title))
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT
)
gravity = Gravity.CENTER
includeFontPadding = false
}
/**
* 弹出清零确认对话框
* @param deviceId 目标设备 ID
+25 -16
View File
@@ -29,48 +29,57 @@
<LinearLayout
android:id="@+id/llScaleContainer"
android:layout_width="match_parent"
android:layout_height="120dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_height="260dp"
android:orientation="vertical"
android:gravity="center"
android:layout_marginTop="5dp"
android:layout_marginBottom="2dp"
android:paddingStart="30dp"
android:background="@drawable/shape_scale_cell">
<TextView
android:id="@+id/tvAddr"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="22sp"
android:textSize="18sp"
android:includeFontPadding="false"
android:textColor="@color/home_sub_title"
tools:text="秤 1" />
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:includeFontPadding="false"
android:textColor="@color/home_sub_title"
android:text="油" />
<TextView
android:id="@+id/tvWeight"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textSize="26sp"
android:textStyle="bold"
android:includeFontPadding="false"
android:layout_marginVertical="5dp"
android:textColor="@color/home_title"
tools:text="100.0 g" />
<TextView
android:id="@+id/tvState"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp"
android:includeFontPadding="false"
android:textColor="@color/home_sub_title"
tools:text="稳定" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/gray_eb"
android:layout_marginTop="10dp" />
<!-- <com.google.android.material.divider.MaterialDivider-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="1dp"-->
<!-- android:background="@color/gray_eb"-->
<!-- android:layout_marginTop="10dp" />-->
</LinearLayout>
@@ -32,10 +32,10 @@
android:layout_marginTop="5dp"
android:overScrollMode="never" />
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/gray_eb"
android:layout_marginTop="10dp" />
<!-- <com.google.android.material.divider.MaterialDivider-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="1dp"-->
<!-- android:background="@color/gray_eb"-->
<!-- android:layout_marginTop="10dp" />-->
</LinearLayout>
+20 -9
View File
@@ -25,18 +25,29 @@
android:textSize="14sp"
tools:text="设备:device_01" />
<!-- 水平排列2个秤格子,高度固定 400dp -->
<!-- 水平排列2个秤格子 -->
<LinearLayout
android:id="@+id/llScaleContainer"
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="5dp" />
android:layout_marginTop="5dp">
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/gray_eb"
android:layout_marginTop="10dp" />
<include
android:id="@+id/cell1"
layout="@layout/list_item_scale22_row"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_margin="2dp" />
<include
android:id="@+id/cell2"
layout="@layout/list_item_scale22_row"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_margin="2dp" />
</LinearLayout>
</LinearLayout>
@@ -32,10 +32,10 @@
android:layout_marginTop="5dp"
android:overScrollMode="never" />
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/gray_eb"
android:layout_marginTop="10dp" />
<!-- <com.google.android.material.divider.MaterialDivider-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="1dp"-->
<!-- android:background="@color/gray_eb"-->
<!-- android:layout_marginTop="10dp" />-->
</LinearLayout>