refactor(adapter): 合并 Scale18/22GridAdapter 为 Seasoning18/22GridAdapter,统一支持调料配置和秤总览双模式
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -106,11 +106,6 @@
|
|||||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||||
android:screenOrientation="portrait"
|
android:screenOrientation="portrait"
|
||||||
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||||
<activity
|
|
||||||
android:name="com.shuwei.dish.match.ui.Scale22GridActivity"
|
|
||||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
|
||||||
android:screenOrientation="portrait"
|
|
||||||
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.shuwei.dish.match.ui.FoodRecognizeActivity"
|
android:name="com.shuwei.dish.match.ui.FoodRecognizeActivity"
|
||||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
package com.shuwei.dish.match.adapter
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
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.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 较小
|
|
||||||
* 点击格子触发清零回调
|
|
||||||
*/
|
|
||||||
class Scale18GridAdapter : BaseQuickAdapter<ScaleData, Scale18GridAdapter.VH>(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<ScaleData?>) {
|
|
||||||
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<ScaleData>) {
|
|
||||||
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<ScaleData>) = 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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<ScaleData, Scale22GridAdapter.VH>(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<ScaleData?>) {
|
|
||||||
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<ScaleData>) {
|
|
||||||
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<ScaleData>) = 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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +1,110 @@
|
|||||||
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.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.content.ContextCompat
|
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.R
|
||||||
import com.shuwei.dish.match.databinding.ListItemSeasoningCellBinding
|
import com.shuwei.dish.match.databinding.ListItemScale22RowBinding
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
import com.shuwei.dish.match.scale.ScaleData
|
||||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调料配置-下层(18格)网格 Adapter
|
* 18格网格 Adapter,同时服务于调料配置页和主设备秤总览页。
|
||||||
* 使用 GridLayoutManager(6列)排列,第1行格子 margin 较大,第2-3行 margin 较小
|
*
|
||||||
* 实体类为 SeasoningEntity,格子显示调料名称 goodsName
|
* @param showNameOnly true = 调料配置模式,仅显示 tvName,隐藏 tvWeight/tvState;
|
||||||
|
* false = 秤总览模式,三行全部显示
|
||||||
*/
|
*/
|
||||||
class Seasoning18GridAdapter : BaseQuickAdapter<SeasoningEntity, Seasoning18GridAdapter.VH>(mutableListOf()) {
|
class Seasoning18GridAdapter(
|
||||||
|
private val showNameOnly: Boolean = false
|
||||||
|
) : BaseQuickAdapter<ScaleData, Seasoning18GridAdapter.VH>(mutableListOf()) {
|
||||||
|
|
||||||
/** 空位哨兵,sort=-1 表示无效 */
|
/** 空位哨兵,address=0 表示无效 */
|
||||||
private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 }
|
private val empty = ScaleData("", 0, 0.0, -1, 0)
|
||||||
|
|
||||||
/** 当前高亮的格子位置,-1 表示无高亮 */
|
/** 当前高亮的格子位置,-1 表示无高亮 */
|
||||||
private var highlightedPosition: Int = -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小) */
|
/** 前6个位置为第1行(viewType=0,margin大),其余为第2-3行(viewType=1,margin小) */
|
||||||
override fun getItemViewType(position: Int, list: List<SeasoningEntity>) =
|
override fun getItemViewType(position: Int, list: List<ScaleData>) =
|
||||||
if (position < 6) 0 else 1
|
if (position < 6) 0 else 1
|
||||||
|
|
||||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
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 margin = if (viewType == 0) 12.dp else 2.dp
|
||||||
val screenWidth = parent.resources.displayMetrics.widthPixels
|
val screenWidth = parent.resources.displayMetrics.widthPixels
|
||||||
val cellSize = (screenWidth - 6 * margin * 2 - 10.dp) / 6
|
val cellSize = (screenWidth - 6 * margin * 2 - 10.dp) / 6
|
||||||
(b.root.layoutParams as? ViewGroup.MarginLayoutParams)?.also {
|
(b.root.layoutParams as? ViewGroup.MarginLayoutParams)?.also {
|
||||||
it.width = cellSize
|
it.width = cellSize
|
||||||
it.height = cellSize
|
it.height = cellSize
|
||||||
// 第1行:顶部间距为0,底部间距2dp,左右保持原有 margin
|
|
||||||
if (viewType == 0) it.setMargins(margin, 0, margin, 2.dp)
|
if (viewType == 0) it.setMargins(margin, 0, margin, 2.dp)
|
||||||
else it.setMargins(margin, margin, margin, margin)
|
else it.setMargins(margin, margin, margin, margin)
|
||||||
}
|
}
|
||||||
val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt()
|
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)
|
val vh = VH(b)
|
||||||
// 点击事件预留,暂不处理
|
|
||||||
b.root.setOnClickListener {
|
b.root.setOnClickListener {
|
||||||
val pos = vh.bindingAdapterPosition
|
val pos = vh.bindingAdapterPosition
|
||||||
if (pos != RecyclerView.NO_POSITION) {
|
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
|
return vh
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) {
|
@SuppressLint("SetTextI18n")
|
||||||
holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-"
|
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) {
|
if (position == highlightedPosition) {
|
||||||
holder.b.tvGoodsName.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
holder.b.root.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
||||||
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.white))
|
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 {
|
} else {
|
||||||
holder.b.tvGoodsName.setBackgroundResource(R.drawable.shape_scale_cell)
|
holder.b.root.setBackgroundResource(R.drawable.shape_scale_cell)
|
||||||
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.home_title))
|
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) {
|
fun highlightPosition(position: Int) {
|
||||||
val prev = highlightedPosition
|
val prev = highlightedPosition
|
||||||
@@ -91,23 +121,46 @@ class Seasoning18GridAdapter : BaseQuickAdapter<SeasoningEntity, Seasoning18Grid
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新指定位置的调料名称并刷新对应格子
|
* 更新指定位置的名称并刷新对应格子
|
||||||
* @param position 格子索引
|
|
||||||
* @param name 新的调料名称
|
|
||||||
*/
|
*/
|
||||||
fun updateItemName(position: Int, name: String) {
|
fun updateItemName(position: Int, name: String) {
|
||||||
items.getOrNull(position)?.goodsName = name
|
items.getOrNull(position)?.let {
|
||||||
notifyItemChanged(position)
|
set(position, it.copy(name = name))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按 SCALE_ORDER_18 的 sort 顺序更新数据,空位用哨兵填充
|
* 按 SCALE_ORDER_18 的 sort 顺序更新数据,空位用哨兵填充
|
||||||
|
* 只更新显示字段有变化的格子,避免全量刷新导致 ViewHolder 重建影响点击监听
|
||||||
*/
|
*/
|
||||||
fun update(seasoningList: List<SeasoningEntity>) {
|
fun update(list: List<ScaleData>) {
|
||||||
val bySort = seasoningList.associateBy { it.sort }
|
val byAddress = list.associateBy { it.address }
|
||||||
val filled = ScaleDeviceConfig.SCALE_ORDER_18.map { sort ->
|
val filled = ScaleDeviceConfig.SCALE_ORDER_18.map { addr ->
|
||||||
bySort[sort] ?: empty.copy().also { it.sort = sort }
|
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<ScaleData>) {
|
||||||
|
update(scales)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stateStr(state: Int) = when (state) {
|
||||||
|
WeightUtil.STATE_STABLE -> "稳定"
|
||||||
|
WeightUtil.STATE_UNSTABLE -> "不稳定"
|
||||||
|
WeightUtil.STATE_OVER_WEIGHT -> "超量"
|
||||||
|
else -> ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
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.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
@@ -9,41 +11,43 @@ 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.R
|
||||||
import com.shuwei.dish.match.databinding.ListItemSeasoningCellBinding
|
import com.shuwei.dish.match.databinding.ListItemScale22RowBinding
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
import com.shuwei.dish.match.scale.ScaleData
|
||||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 调料配置-上层(22格)网格 Adapter
|
* 22格网格 Adapter,同时服务于调料配置页和主设备秤总览页。
|
||||||
* 使用 FlexboxLayoutManager 按列排列,前4个格子较大,其余较小
|
|
||||||
* 实体类为 SeasoningEntity,格子显示调料名称 goodsName
|
|
||||||
*
|
*
|
||||||
* @param largeSize 前4个大格子的尺寸(px)
|
* @param largeSize 前4个大格子的尺寸(px)
|
||||||
* @param smallSize 其余小格子的尺寸(px)
|
* @param smallSize 其余小格子的尺寸(px)
|
||||||
|
* @param showNameOnly true = 调料配置模式,仅显示 tvName,隐藏 tvWeight/tvState;
|
||||||
|
* false = 秤总览模式,三行全部显示
|
||||||
*/
|
*/
|
||||||
class Seasoning22GridAdapter(
|
class Seasoning22GridAdapter(
|
||||||
private val largeSize: Int,
|
private val largeSize: Int,
|
||||||
private val smallSize: Int
|
private val smallSize: Int,
|
||||||
) : BaseQuickAdapter<SeasoningEntity, Seasoning22GridAdapter.VH>(mutableListOf()) {
|
private val showNameOnly: Boolean = false
|
||||||
|
) : BaseQuickAdapter<ScaleData, Seasoning22GridAdapter.VH>(mutableListOf()) {
|
||||||
|
|
||||||
/** 空位哨兵,sort=-1 表示无效 */
|
/** 空位哨兵,address=0 表示无效 */
|
||||||
private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 }
|
private val empty = ScaleData("", 0, 0.0, -1, 0)
|
||||||
|
|
||||||
/** 当前高亮的格子位置,-1 表示无高亮 */
|
/** 当前高亮的格子位置,-1 表示无高亮 */
|
||||||
private var highlightedPosition: Int = -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) */
|
/** 前4个位置为大格子(viewType=1),其余为小格子(viewType=0) */
|
||||||
override fun getItemViewType(position: Int, list: List<SeasoningEntity>) =
|
override fun getItemViewType(position: Int, list: List<ScaleData>) =
|
||||||
if (position < 4) 1 else 0
|
if (position < 4) 1 else 0
|
||||||
|
|
||||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
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
|
val cellSize = if (viewType == 1) largeSize else smallSize
|
||||||
(b.root.layoutParams as? FlexboxLayoutManager.LayoutParams)?.apply {
|
(b.root.layoutParams as? FlexboxLayoutManager.LayoutParams)?.apply {
|
||||||
width = cellSize
|
width = cellSize
|
||||||
@@ -51,33 +55,59 @@ class Seasoning22GridAdapter(
|
|||||||
setMargins(2.dp, 2.dp, 2.dp, 2.dp)
|
setMargins(2.dp, 2.dp, 2.dp, 2.dp)
|
||||||
}
|
}
|
||||||
val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt()
|
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)
|
val vh = VH(b)
|
||||||
// 点击事件预留,暂不处理
|
|
||||||
b.root.setOnClickListener {
|
b.root.setOnClickListener {
|
||||||
val pos = vh.bindingAdapterPosition
|
val pos = vh.bindingAdapterPosition
|
||||||
if (pos != RecyclerView.NO_POSITION) {
|
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
|
return vh
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) {
|
@SuppressLint("SetTextI18n")
|
||||||
holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-"
|
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) {
|
if (position == highlightedPosition) {
|
||||||
holder.b.tvGoodsName.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
holder.b.root.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
||||||
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.white))
|
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 {
|
} else {
|
||||||
holder.b.tvGoodsName.setBackgroundResource(R.drawable.shape_scale_cell)
|
holder.b.root.setBackgroundResource(R.drawable.shape_scale_cell)
|
||||||
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.home_title))
|
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) {
|
fun highlightPosition(position: Int) {
|
||||||
val prev = highlightedPosition
|
val prev = highlightedPosition
|
||||||
@@ -94,23 +124,46 @@ class Seasoning22GridAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新指定位置的调料名称并刷新对应格子
|
* 更新指定位置的名称并刷新对应格子
|
||||||
* @param position 格子索引
|
|
||||||
* @param name 新的调料名称
|
|
||||||
*/
|
*/
|
||||||
fun updateItemName(position: Int, name: String) {
|
fun updateItemName(position: Int, name: String) {
|
||||||
items.getOrNull(position)?.goodsName = name
|
items.getOrNull(position)?.let {
|
||||||
notifyItemChanged(position)
|
set(position, it.copy(name = name))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按 SCALE_ORDER_22 的 sort 顺序更新数据,空位用哨兵填充
|
* 按 SCALE_ORDER_22 的 sort 顺序更新数据,空位用哨兵填充
|
||||||
|
* 只更新显示字段有变化的格子,避免全量刷新导致 ViewHolder 重建影响点击监听
|
||||||
*/
|
*/
|
||||||
fun update(seasoningList: List<SeasoningEntity>) {
|
fun update(list: List<ScaleData>) {
|
||||||
val bySort = seasoningList.associateBy { it.sort }
|
val byAddress = list.associateBy { it.address }
|
||||||
val filled = ScaleDeviceConfig.SCALE_ORDER_22.map { sort ->
|
val filled = ScaleDeviceConfig.SCALE_ORDER_22.map { addr ->
|
||||||
bySort[sort] ?: empty.copy().also { it.sort = sort }
|
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<ScaleData>) {
|
||||||
|
update(scales)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stateStr(state: Int) = when (state) {
|
||||||
|
WeightUtil.STATE_STABLE -> "稳定"
|
||||||
|
WeightUtil.STATE_UNSTABLE -> "不稳定"
|
||||||
|
WeightUtil.STATE_OVER_WEIGHT -> "超量"
|
||||||
|
else -> ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,5 +15,6 @@ data class ScaleData(
|
|||||||
val weight: Double,
|
val weight: Double,
|
||||||
val state: Int,
|
val state: Int,
|
||||||
val ts: Long,
|
val ts: Long,
|
||||||
val ip: String = ""
|
val ip: String = "",
|
||||||
|
val name: String? = null
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import com.google.android.flexbox.FlexDirection
|
|||||||
import com.google.android.flexbox.FlexWrap
|
import com.google.android.flexbox.FlexWrap
|
||||||
import com.google.android.flexbox.FlexboxLayoutManager
|
import com.google.android.flexbox.FlexboxLayoutManager
|
||||||
import com.shuwei.dish.match.R
|
import com.shuwei.dish.match.R
|
||||||
import com.shuwei.dish.match.adapter.Scale18GridAdapter
|
import com.shuwei.dish.match.adapter.Seasoning18GridAdapter
|
||||||
import com.shuwei.dish.match.adapter.Scale22GridAdapter
|
import com.shuwei.dish.match.adapter.Seasoning22GridAdapter
|
||||||
import com.shuwei.dish.match.base.BaseActivity
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
import com.shuwei.dish.match.dialog.CommonDialog
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
import com.shuwei.dish.match.databinding.ActivityMasterScaleBinding
|
import com.shuwei.dish.match.databinding.ActivityMasterScaleBinding
|
||||||
@@ -249,7 +249,7 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
// ── ViewHolder: 22个秤 ─────────────────────────────────────────────────
|
// ── ViewHolder: 22个秤 ─────────────────────────────────────────────────
|
||||||
|
|
||||||
private inner class Scale22VH(val b: ListTypeScale22Binding) : RecyclerView.ViewHolder(b.root) {
|
private inner class Scale22VH(val b: ListTypeScale22Binding) : RecyclerView.ViewHolder(b.root) {
|
||||||
private var innerAdapter: Scale22GridAdapter? = null
|
private var innerAdapter: Seasoning22GridAdapter? = null
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun bind(group: DeviceGroup) {
|
fun bind(group: DeviceGroup) {
|
||||||
@@ -269,8 +269,8 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
alignItems = AlignItems.FLEX_START
|
alignItems = AlignItems.FLEX_START
|
||||||
}
|
}
|
||||||
b.rvScales.itemAnimator = null
|
b.rvScales.itemAnimator = null
|
||||||
innerAdapter = Scale22GridAdapter(largeSize, smallSize).apply {
|
innerAdapter = Seasoning22GridAdapter(largeSize, smallSize, showNameOnly = false).apply {
|
||||||
onItemClick = { scale ->
|
onItemClick = { scale, _ ->
|
||||||
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,7 +288,7 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
// ── ViewHolder: 18个秤 ─────────────────────────────────────────────────
|
// ── ViewHolder: 18个秤 ─────────────────────────────────────────────────
|
||||||
|
|
||||||
private inner class Scale18VH(val b: ListTypeScale18Binding) : RecyclerView.ViewHolder(b.root) {
|
private inner class Scale18VH(val b: ListTypeScale18Binding) : RecyclerView.ViewHolder(b.root) {
|
||||||
private var innerAdapter: Scale18GridAdapter? = null
|
private var innerAdapter: Seasoning18GridAdapter? = null
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun bind(group: DeviceGroup) {
|
fun bind(group: DeviceGroup) {
|
||||||
@@ -303,8 +303,8 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
b.rvScales.layoutManager = gridLayoutManager
|
b.rvScales.layoutManager = gridLayoutManager
|
||||||
b.rvScales.itemAnimator = null
|
b.rvScales.itemAnimator = null
|
||||||
innerAdapter = Scale18GridAdapter().apply {
|
innerAdapter = Seasoning18GridAdapter(showNameOnly = false).apply {
|
||||||
onItemClick = { scale ->
|
onItemClick = { scale, _ ->
|
||||||
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<ScaleData?>()
|
|
||||||
|
|
||||||
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<ScaleData?>,
|
|
||||||
private val largeSize: Int, // 大格子宽高(px)
|
|
||||||
private val smallSize: Int // 小格子宽高(px)
|
|
||||||
) : RecyclerView.Adapter<Scale22Adapter.VH>() {
|
|
||||||
|
|
||||||
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}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,8 +13,8 @@ import com.google.android.flexbox.AlignItems
|
|||||||
import com.google.android.flexbox.FlexDirection
|
import com.google.android.flexbox.FlexDirection
|
||||||
import com.google.android.flexbox.FlexWrap
|
import com.google.android.flexbox.FlexWrap
|
||||||
import com.google.android.flexbox.FlexboxLayoutManager
|
import com.google.android.flexbox.FlexboxLayoutManager
|
||||||
import com.shuwei.dish.match.adapter.Scale18GridAdapter
|
import com.shuwei.dish.match.adapter.Seasoning18GridAdapter
|
||||||
import com.shuwei.dish.match.adapter.Scale22GridAdapter
|
import com.shuwei.dish.match.adapter.Seasoning22GridAdapter
|
||||||
import com.shuwei.dish.match.adapter.ScaleRowAdapter
|
import com.shuwei.dish.match.adapter.ScaleRowAdapter
|
||||||
import com.shuwei.dish.match.base.BaseActivity
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
import com.shuwei.dish.match.base.GlobalData
|
import com.shuwei.dish.match.base.GlobalData
|
||||||
@@ -40,9 +40,9 @@ class SlaveActivity : BaseActivity() {
|
|||||||
private val linearAdapter = ScaleRowAdapter()
|
private val linearAdapter = ScaleRowAdapter()
|
||||||
|
|
||||||
/** 22个秤网格 adapter */
|
/** 22个秤网格 adapter */
|
||||||
private var scale22Adapter: Scale22GridAdapter? = null
|
private var scale22Adapter: Seasoning22GridAdapter? = null
|
||||||
/** 18个秤网格 adapter */
|
/** 18个秤网格 adapter */
|
||||||
private var scale18Adapter: Scale18GridAdapter? = null
|
private var scale18Adapter: Seasoning18GridAdapter? = null
|
||||||
|
|
||||||
/** 已连接的主设备数量(有连接即代表主设备在线) */
|
/** 已连接的主设备数量(有连接即代表主设备在线) */
|
||||||
private var masterConnected = false
|
private var masterConnected = false
|
||||||
@@ -95,8 +95,8 @@ class SlaveActivity : BaseActivity() {
|
|||||||
flexWrap = FlexWrap.WRAP
|
flexWrap = FlexWrap.WRAP
|
||||||
alignItems = AlignItems.FLEX_START
|
alignItems = AlignItems.FLEX_START
|
||||||
}
|
}
|
||||||
scale22Adapter = Scale22GridAdapter(largeSize, smallSize).also {
|
scale22Adapter = Seasoning22GridAdapter(largeSize, smallSize, showNameOnly = false).also {
|
||||||
it.onItemClick = { scale -> showTareDialog(scale.address) }
|
it.onItemClick = { scale, _ -> showTareDialog(scale.address) }
|
||||||
}
|
}
|
||||||
binding.rvScaleList.adapter = scale22Adapter
|
binding.rvScaleList.adapter = scale22Adapter
|
||||||
}
|
}
|
||||||
@@ -107,8 +107,8 @@ class SlaveActivity : BaseActivity() {
|
|||||||
override fun getSpanSize(position: Int) = 1
|
override fun getSpanSize(position: Int) = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scale18Adapter = Scale18GridAdapter().also {
|
scale18Adapter = Seasoning18GridAdapter(showNameOnly = false).also {
|
||||||
it.onItemClick = { scale -> showTareDialog(scale.address) }
|
it.onItemClick = { scale, _ -> showTareDialog(scale.address) }
|
||||||
}
|
}
|
||||||
binding.rvScaleList.adapter = scale18Adapter
|
binding.rvScaleList.adapter = scale18Adapter
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
|||||||
// private lateinit var appViewModel: AppViewModel
|
// private lateinit var appViewModel: AppViewModel
|
||||||
|
|
||||||
private val adapter22 by lazy { buildAdapter22() }
|
private val adapter22 by lazy { buildAdapter22() }
|
||||||
private val adapter18 by lazy { Seasoning18GridAdapter() }
|
private val adapter18 by lazy { Seasoning18GridAdapter(showNameOnly = true) }
|
||||||
|
|
||||||
/** 各秤的上一次重量快照,key 格式 "{deviceId}#{address}",用于计算 delta */
|
/** 各秤的上一次重量快照,key 格式 "{deviceId}#{address}",用于计算 delta */
|
||||||
private val weightSnapshot = mutableMapOf<String, Double>()
|
private val weightSnapshot = mutableMapOf<String, Double>()
|
||||||
@@ -96,7 +96,7 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
|||||||
val usable = resources.displayMetrics.widthPixels - 8 * 4.dp - 10.dp
|
val usable = resources.displayMetrics.widthPixels - 8 * 4.dp - 10.dp
|
||||||
val smallSize = usable / 9
|
val smallSize = usable / 9
|
||||||
val largeSize = (usable - smallSize * 6) / 2
|
val largeSize = (usable - smallSize * 6) / 2
|
||||||
return Seasoning22GridAdapter(largeSize, smallSize)
|
return Seasoning22GridAdapter(largeSize, smallSize, showNameOnly = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 配置两个 RecyclerView */
|
/** 配置两个 RecyclerView */
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:background="@color/bg_color">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:text="22格秤数据"
|
|
||||||
android:textColor="@color/home_title"
|
|
||||||
android:textSize="36sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/ivBack"
|
|
||||||
android:layout_width="70dp"
|
|
||||||
android:layout_height="60dp"
|
|
||||||
android:paddingStart="30dp"
|
|
||||||
android:paddingEnd="10dp"
|
|
||||||
android:src="@drawable/ic_back"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/tvTitle"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/tvTitle"
|
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/rvScale22"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:layout_marginStart="2dp"
|
|
||||||
android:layout_marginEnd="2dp"
|
|
||||||
android:overScrollMode="never"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/tvTitle"
|
|
||||||
tools:listitem="@layout/list_item_scale22_row" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
android:background="@drawable/shape_scale_cell">
|
android:background="@drawable/shape_scale_cell">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvAddr"
|
android:id="@+id/tvName"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
|||||||
Reference in New Issue
Block a user