Compare commits
5
Commits
b775b20ff1
...
7373ccacbb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7373ccacbb | ||
|
|
ad63639f66 | ||
|
|
c3f3b1215b | ||
|
|
cba5bda757 | ||
|
|
7fd2f02b87 |
@@ -111,6 +111,11 @@
|
|||||||
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.FoodRecognizeActivity"
|
||||||
|
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name="com.shuwei.dish.match.ui.SlaveActivity"
|
android:name="com.shuwei.dish.match.ui.SlaveActivity"
|
||||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.shuwei.dish.match.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.chad.library.adapter4.BaseQuickAdapter
|
||||||
|
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||||
|
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)
|
||||||
|
|
||||||
|
/** 点击回调,在 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 ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stateStr(state: Int) = when (state) {
|
||||||
|
WeightUtil.STATE_STABLE -> "稳定"
|
||||||
|
WeightUtil.STATE_UNSTABLE -> "不稳定"
|
||||||
|
WeightUtil.STATE_OVER_WEIGHT -> "超量"
|
||||||
|
else -> "$state"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package com.shuwei.dish.match.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
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.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)
|
||||||
|
|
||||||
|
/** 点击回调,在 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 ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stateStr(state: Int) = when (state) {
|
||||||
|
WeightUtil.STATE_STABLE -> "稳定"
|
||||||
|
WeightUtil.STATE_UNSTABLE -> "不稳定"
|
||||||
|
WeightUtil.STATE_OVER_WEIGHT -> "超量"
|
||||||
|
else -> "$state"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.shuwei.dish.match.adapter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.chad.library.adapter4.BaseQuickAdapter
|
||||||
|
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||||
|
import com.shuwei.dish.match.databinding.ListItemScaleRowBinding
|
||||||
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线性列表秤数据 Adapter
|
||||||
|
* 用于 SlaveActivity 非22/18格子设备的秤列表展示
|
||||||
|
*/
|
||||||
|
class ScaleRowAdapter : BaseQuickAdapter<ScaleRowAdapter.ScaleItem, ScaleRowAdapter.VH>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 秤数据条目
|
||||||
|
* @param address 秤地址
|
||||||
|
* @param weight 重量(克)
|
||||||
|
* @param state 状态码
|
||||||
|
*/
|
||||||
|
data class ScaleItem(val address: Int, val weight: Double, val state: Int)
|
||||||
|
|
||||||
|
inner class VH(val b: ListItemScaleRowBinding) : QuickViewHolder(b.root)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||||
|
return VH(ListItemScaleRowBinding.inflate(LayoutInflater.from(context), parent, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: VH, position: Int, item: ScaleItem?) {
|
||||||
|
item ?: return
|
||||||
|
holder.b.tvScaleLabel.text = "秤 ${item.address}"
|
||||||
|
holder.b.tvWeight.text = "${item.weight} g"
|
||||||
|
holder.b.tvState.text = when (item.state) {
|
||||||
|
WeightUtil.STATE_STABLE -> "稳定"
|
||||||
|
WeightUtil.STATE_UNSTABLE -> "不稳定"
|
||||||
|
WeightUtil.STATE_OVER_WEIGHT -> "超量"
|
||||||
|
else -> "${item.state}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import com.shuwei.dish.match.entity.SeasoningEntity
|
|||||||
import com.shuwei.dish.match.net.NetViewModel
|
import com.shuwei.dish.match.net.NetViewModel
|
||||||
import com.shuwei.dish.match.ui.InitActivity
|
import com.shuwei.dish.match.ui.InitActivity
|
||||||
import com.shuwei.dish.match.utils.ActivityManager
|
import com.shuwei.dish.match.utils.ActivityManager
|
||||||
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
import com.shuwei.dish.match.utils.ext.gone
|
import com.shuwei.dish.match.utils.ext.gone
|
||||||
import com.shuwei.dish.match.utils.ext.invisible
|
import com.shuwei.dish.match.utils.ext.invisible
|
||||||
import com.shuwei.dish.match.utils.ext.visible
|
import com.shuwei.dish.match.utils.ext.visible
|
||||||
@@ -65,6 +66,10 @@ open class BaseActivity : AppCompatActivity() {
|
|||||||
// systemBarsBehavior =
|
// systemBarsBehavior =
|
||||||
// WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE // 滑动时临时显示
|
// WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE // 滑动时临时显示
|
||||||
// }
|
// }
|
||||||
|
// 主设备垂直 padding 30dp,子设备 15dp
|
||||||
|
val verticalPadding = if (GlobalData.deviceRole == DeviceRole.MASTER) 30.dp else 10.dp
|
||||||
|
binding.tvLeftTime.setPadding(0, verticalPadding, 0, verticalPadding)
|
||||||
|
binding.tvRightTime.setPadding(0, verticalPadding, 0, verticalPadding)
|
||||||
binding.llTitleBar.gone()
|
binding.llTitleBar.gone()
|
||||||
binding.ivBack.setOnClickListener { finish() }
|
binding.ivBack.setOnClickListener { finish() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,11 @@ import android.graphics.drawable.ColorDrawable
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.Window
|
import android.view.Window
|
||||||
import android.view.WindowManager
|
import android.widget.LinearLayout
|
||||||
import androidx.annotation.StyleRes
|
import androidx.annotation.StyleRes
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.core.view.updateLayoutParams
|
||||||
|
import com.shuwei.dish.match.base.DeviceRole
|
||||||
|
import com.shuwei.dish.match.base.GlobalData
|
||||||
import com.shuwei.dish.match.databinding.DialogCommonBinding
|
import com.shuwei.dish.match.databinding.DialogCommonBinding
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
@@ -20,53 +22,74 @@ open class CommonDialog(
|
|||||||
|
|
||||||
private lateinit var binding: DialogCommonBinding
|
private lateinit var binding: DialogCommonBinding
|
||||||
|
|
||||||
|
private var titleText: String? = null
|
||||||
|
private var contentText: String? = null
|
||||||
|
private var negativeText = "取消"
|
||||||
|
private var positiveText = "确认"
|
||||||
|
private var negativeClick: (() -> Unit)? = null
|
||||||
|
private var positiveClick: (() -> Unit)? = null
|
||||||
|
private var dismissCallback: (() -> Unit)? = null
|
||||||
|
|
||||||
|
/** 设置标题,为空时隐藏 */
|
||||||
|
fun setTitle(text: String): CommonDialog = apply { titleText = text }
|
||||||
|
|
||||||
|
/** 设置内容,为空时隐藏 */
|
||||||
|
fun setContent(text: String): CommonDialog = apply { contentText = text }
|
||||||
|
|
||||||
|
/** 设置左侧取消按钮文字及点击回调 */
|
||||||
|
fun setNegativeButton(text: String, onClick: (() -> Unit)? = null): CommonDialog = apply {
|
||||||
|
negativeText = text
|
||||||
|
negativeClick = onClick
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置右侧确认按钮文字及点击回调 */
|
||||||
|
fun setPositiveButton(text: String, onClick: (() -> Unit)? = null): CommonDialog = apply {
|
||||||
|
positiveText = text
|
||||||
|
positiveClick = onClick
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 设置弹窗消失回调 */
|
||||||
|
fun setOnDismissCallback(callback: () -> Unit): CommonDialog = apply {
|
||||||
|
dismissCallback = callback
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
binding = DialogCommonBinding.inflate(layoutInflater)
|
binding = DialogCommonBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
window?.run {
|
window?.run {
|
||||||
// decorView.setPadding(0.dp, 0, 0.dp, 0)
|
val screenWidth = context.resources.displayMetrics.widthPixels
|
||||||
|
val isMaster = GlobalData.deviceRole == DeviceRole.MASTER
|
||||||
attributes = attributes.apply {
|
attributes = attributes.apply {
|
||||||
width = 708.dp
|
width = if (isMaster) (screenWidth * 0.72f).toInt() else (screenWidth * 0.60f).toInt()
|
||||||
}
|
}
|
||||||
setBackgroundDrawable(ColorDrawable())
|
setBackgroundDrawable(ColorDrawable())
|
||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
// setFlags(
|
|
||||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
|
||||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
|
||||||
// )
|
|
||||||
}
|
}
|
||||||
|
val verticalMargin = if (GlobalData.deviceRole == DeviceRole.MASTER) 35.dp else 20.dp
|
||||||
binding.llContent.run {
|
binding.tvTitle.updateLayoutParams<LinearLayout.LayoutParams> { topMargin = verticalMargin }
|
||||||
removeAllViews()
|
binding.tvContent.updateLayoutParams<LinearLayout.LayoutParams> { bottomMargin = verticalMargin }
|
||||||
addView(messageView)
|
binding.btnLeft.setOnClickListener {
|
||||||
|
negativeClick?.invoke()
|
||||||
|
dismiss()
|
||||||
}
|
}
|
||||||
addListener()
|
binding.btnRight.setOnClickListener {
|
||||||
|
positiveClick?.invoke()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
setOnDismissListener { dismissCallback?.invoke() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addListener() {
|
|
||||||
binding.btnLeft.setOnClickListener { onLeftClick?.invoke() }
|
|
||||||
binding.btnRight.setOnClickListener { onRightClick?.invoke() }
|
|
||||||
setOnDismissListener { onDismiss?.invoke() }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun show() {
|
override fun show() {
|
||||||
super.show()
|
super.show()
|
||||||
binding.btnLeft.text = leftText
|
binding.tvTitle.text = titleText
|
||||||
binding.btnRight.text = rightText
|
binding.tvContent.text = contentText
|
||||||
|
binding.tvTitle.visibility = if (titleText.isNullOrEmpty()) View.GONE else View.VISIBLE
|
||||||
|
binding.tvContent.visibility = if (contentText.isNullOrEmpty()) View.GONE else View.VISIBLE
|
||||||
|
binding.btnLeft.text = negativeText
|
||||||
|
binding.btnRight.text = positiveText
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageView: View? = null
|
|
||||||
var onLeftClick: (() -> Unit)? = null
|
|
||||||
var onRightClick: (() -> Unit)? = null
|
|
||||||
|
|
||||||
var leftText = ""
|
|
||||||
var rightText = ""
|
|
||||||
|
|
||||||
var onDismiss: (() -> Unit)? = null
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open class SafeDialog(activity: Activity, @StyleRes themeResId: Int) :
|
open class SafeDialog(activity: Activity, @StyleRes themeResId: Int) :
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
package com.shuwei.dish.match.dialog
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding
|
|
||||||
|
|
||||||
object DialogTool {
|
|
||||||
|
|
||||||
fun load(
|
|
||||||
context: Context,
|
|
||||||
title:String,
|
|
||||||
content:String,
|
|
||||||
leftBtnText: String,
|
|
||||||
rightBtnText: String,
|
|
||||||
leftBtnClick: () -> Unit = {},
|
|
||||||
rightBtnClick: () -> Unit = {}
|
|
||||||
) {
|
|
||||||
val layoutInflater = LayoutInflater.from(context)
|
|
||||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
|
||||||
remindBinding.tvDialogTitle.text = title
|
|
||||||
remindBinding.tvDialogContent.text = content
|
|
||||||
CommonDialog(context).apply {
|
|
||||||
messageView = remindBinding.root
|
|
||||||
leftText = leftBtnText
|
|
||||||
rightText = rightBtnText
|
|
||||||
onLeftClick = {
|
|
||||||
leftBtnClick()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
onRightClick = {
|
|
||||||
rightBtnClick()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
onDismiss = { }
|
|
||||||
}.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -66,10 +66,10 @@ class LoadingDialog(
|
|||||||
setCancelable(true)
|
setCancelable(true)
|
||||||
window?.run {
|
window?.run {
|
||||||
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
|
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
|
||||||
setFlags(
|
// setFlags(
|
||||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
setOnDismissListener {
|
setOnDismissListener {
|
||||||
onDismiss()
|
onDismiss()
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.shuwei.dish.match.scale
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 各子设备的固定 ID 与秤地址排列顺序配置
|
||||||
|
* MasterScaleActivity 和 SlaveActivity 共用此配置,避免重复定义
|
||||||
|
*/
|
||||||
|
object ScaleDeviceConfig {
|
||||||
|
|
||||||
|
const val DEVICE_ID_2 = "8fc2ab34-2137-3112-acca-f884ea8736d4"
|
||||||
|
const val DEVICE_ID_22 = "a46fa55c-113c-3511-bb1f-41e5eff77c4b"
|
||||||
|
const val DEVICE_ID_18 = "1038da9f-c6eb-326e-a1d9-d6d3af978b22"
|
||||||
|
const val DEVICE_ID_1 = "7cc0f6ea-f13d-3013-a867-fc998eb554ac"
|
||||||
|
|
||||||
|
/** 22个秤的显示顺序(按物理位置排列) */
|
||||||
|
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)
|
||||||
|
|
||||||
|
/** 18个秤的显示顺序(按物理位置排列) */
|
||||||
|
val SCALE_ORDER_18 = listOf(6, 5, 4, 3, 2, 1, 12, 11, 10, 9, 8, 7, 18, 17, 16, 15, 14, 13)
|
||||||
|
|
||||||
|
/** 设备在列表中的显示顺序 */
|
||||||
|
val DEVICE_ORDER = listOf(DEVICE_ID_2, DEVICE_ID_22, DEVICE_ID_18, DEVICE_ID_1)
|
||||||
|
}
|
||||||
@@ -100,6 +100,7 @@ object ScaleServiceManager {
|
|||||||
// WebSocket 客户端
|
// WebSocket 客户端
|
||||||
val client = ScaleWebSocketClient().also {
|
val client = ScaleWebSocketClient().also {
|
||||||
it.onScaleData = { data -> aggregator?.onRemoteScaleData(data) }
|
it.onScaleData = { data -> aggregator?.onRemoteScaleData(data) }
|
||||||
|
it.onDeviceDisconnected = { remoteId -> aggregator?.removeDevice(remoteId) }
|
||||||
}
|
}
|
||||||
wsClient = client
|
wsClient = client
|
||||||
|
|
||||||
@@ -141,12 +142,13 @@ object ScaleServiceManager {
|
|||||||
* @param deviceId 目标设备 ID
|
* @param deviceId 目标设备 ID
|
||||||
* @param address 目标秤地址
|
* @param address 目标秤地址
|
||||||
*/
|
*/
|
||||||
fun sendTare(deviceId: String, address: Int) {
|
fun sendTare(deviceId: String, address: Int): Boolean {
|
||||||
if (deviceId == GlobalData.deviceId) {
|
return if (deviceId == GlobalData.deviceId) {
|
||||||
com.shuwei.dish.match.utils.WeightUtil.tareTwo(address)
|
com.shuwei.dish.match.utils.WeightUtil.tareTwo(address)
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
val cmd = ScaleCommand(ScaleCommand.CMD_TARE, deviceId, address)
|
val cmd = ScaleCommand(ScaleCommand.CMD_TARE, deviceId, address)
|
||||||
wsClient?.sendCommand(deviceId, cmd)
|
wsClient?.sendCommand(deviceId, cmd) ?: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ class ScaleWebSocketClient {
|
|||||||
/** 收到秤数据时的回调,在子线程调用 */
|
/** 收到秤数据时的回调,在子线程调用 */
|
||||||
var onScaleData: ((data: ScaleData) -> Unit)? = null
|
var onScaleData: ((data: ScaleData) -> Unit)? = null
|
||||||
|
|
||||||
|
/** 设备断线时的回调(连接失败或关闭),在子线程调用 */
|
||||||
|
var onDeviceDisconnected: ((deviceId: String) -> Unit)? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 连接到指定子设备
|
* 连接到指定子设备
|
||||||
* @param deviceId 子设备 ID
|
* @param deviceId 子设备 ID
|
||||||
@@ -85,12 +88,14 @@ class ScaleWebSocketClient {
|
|||||||
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||||
Log.w(TAG, "连接子设备失败: $deviceId, ${t.message}")
|
Log.w(TAG, "连接子设备失败: $deviceId, ${t.message}")
|
||||||
connections.remove(deviceId)
|
connections.remove(deviceId)
|
||||||
|
onDeviceDisconnected?.invoke(deviceId)
|
||||||
scheduleReconnect(deviceId, host, port)
|
scheduleReconnect(deviceId, host, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
||||||
Log.d(TAG, "子设备连接已关闭: $deviceId, reason=$reason")
|
Log.d(TAG, "子设备连接已关闭: $deviceId, reason=$reason")
|
||||||
connections.remove(deviceId)
|
connections.remove(deviceId)
|
||||||
|
onDeviceDisconnected?.invoke(deviceId)
|
||||||
scheduleReconnect(deviceId, host, port)
|
scheduleReconnect(deviceId, host, port)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import androidx.core.view.forEach
|
|||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import com.shuwei.dish.match.base.BaseActivity
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
import com.shuwei.dish.match.base.BaseApp
|
import com.shuwei.dish.match.base.BaseApp
|
||||||
import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding
|
|
||||||
import com.shuwei.dish.match.db.AppRepository
|
import com.shuwei.dish.match.db.AppRepository
|
||||||
import com.shuwei.dish.match.dialog.CommonDialog
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||||
@@ -169,21 +168,13 @@ class DeviceConfigActivity : BaseActivity() {
|
|||||||
val detector = MultiClickDetector(targetCount = 10, intervalMs = 800)
|
val detector = MultiClickDetector(targetCount = 10, intervalMs = 800)
|
||||||
|
|
||||||
private fun defaultDataSettingDialog() {
|
private fun defaultDataSettingDialog() {
|
||||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
CommonDialog(this)
|
||||||
remindBinding.tvDialogTitle.text = "温馨提示"
|
.setTitle("温馨提示")
|
||||||
remindBinding.tvDialogContent.text =
|
.setContent("您好,使用默认配置作为您的调料数据,则已有配置将被清除,确认吗?")
|
||||||
"您好,使用默认配置作为您的调料数据,则已有配置将被清除,确认吗?"
|
.setNegativeButton("取消")
|
||||||
CommonDialog(this).apply {
|
.setPositiveButton("确认") { resetList() }
|
||||||
messageView = remindBinding.root
|
.setOnDismissCallback { hideStatusBar() }
|
||||||
leftText = "取消"
|
.show()
|
||||||
rightText = "确认"
|
|
||||||
onLeftClick = { dismiss() }
|
|
||||||
onRightClick = {
|
|
||||||
dismiss()
|
|
||||||
resetList()
|
|
||||||
}
|
|
||||||
onDismiss = { hideStatusBar() }
|
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resetList() {
|
private fun resetList() {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import com.shuwei.dish.match.adapter.DishPartAdapter
|
|||||||
import com.shuwei.dish.match.base.BaseActivity
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
import com.shuwei.dish.match.base.BaseApp
|
import com.shuwei.dish.match.base.BaseApp
|
||||||
import com.shuwei.dish.match.databinding.ActivityDishSamplingBinding
|
import com.shuwei.dish.match.databinding.ActivityDishSamplingBinding
|
||||||
import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding
|
|
||||||
import com.shuwei.dish.match.db.AppRepository
|
import com.shuwei.dish.match.db.AppRepository
|
||||||
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
||||||
import com.shuwei.dish.match.dialog.CommonDialog
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
@@ -130,23 +129,18 @@ class DishSamplingActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun saveDataRemindDialog() {
|
private fun saveDataRemindDialog() {
|
||||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
CommonDialog(this)
|
||||||
remindBinding.tvDialogTitle.text = "温馨提示"
|
.setTitle("温馨提示")
|
||||||
remindBinding.tvDialogContent.text = "您好,当前页面存在未保存的数据,确认返回吗?"
|
.setContent("您好,当前页面存在未保存的数据,确认返回吗?")
|
||||||
CommonDialog(this).apply {
|
.setNegativeButton("取消")
|
||||||
messageView = remindBinding.root
|
.setPositiveButton("确认") {
|
||||||
leftText = "取消"
|
|
||||||
rightText = "确认"
|
|
||||||
onLeftClick = { dismiss() }
|
|
||||||
onRightClick = {
|
|
||||||
dismiss()
|
|
||||||
if (pageFrom == HOME) {
|
if (pageFrom == HOME) {
|
||||||
startActivity<SamplingListActivity>()
|
startActivity<SamplingListActivity>()
|
||||||
}
|
}
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
onDismiss = { hideStatusBar() }
|
.setOnDismissCallback { hideStatusBar() }
|
||||||
}.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
package com.shuwei.dish.match.ui
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.shuwei.dish.match.adapter.GoodsInfoSearchAdapter
|
||||||
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
|
import com.shuwei.dish.match.databinding.ActivityFoodRecognizeBinding
|
||||||
|
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
||||||
|
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
||||||
|
import com.shuwei.dish.match.utils.ActivityManager
|
||||||
|
import com.shuwei.dish.match.utils.AddressUtil
|
||||||
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
|
import com.shuwei.dish.match.utils.ext.clickWithDebounce
|
||||||
|
import com.shuwei.dish.match.utils.ext.gone
|
||||||
|
import com.shuwei.dish.match.utils.ext.visible
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 食材识别结果页面
|
||||||
|
* 展示识别到的食材照片、食材列表、选中食材名称及秤1重量
|
||||||
|
*/
|
||||||
|
class FoodRecognizeActivity : BaseActivity() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "FoodRecognizeActivity"
|
||||||
|
private const val EXTRA_IMAGE_URI = "extra_image_uri"
|
||||||
|
const val EXTRA_SELECTED_ITEM = "extra_selected_item"
|
||||||
|
|
||||||
|
/** 内存缓存食材列表,避免通过 Intent 序列化传递大数据 */
|
||||||
|
private var pendingGoodsList: ArrayList<CookFoodGoodsEntity>? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动本页面,通过 onResult 回调返回用户选中的食材
|
||||||
|
* @param activity 宿主 Activity
|
||||||
|
* @param imageUri 食材照片路径(可为空)
|
||||||
|
* @param goodsList 识别到的食材列表
|
||||||
|
* @param onResult 页面关闭后回调,携带用户选中的食材(未选中时为 null)
|
||||||
|
*/
|
||||||
|
fun start(
|
||||||
|
activity: BaseActivity,
|
||||||
|
imageUri: String? = null,
|
||||||
|
goodsList: ArrayList<CookFoodGoodsEntity> = arrayListOf(),
|
||||||
|
onResult: (CookFoodGoodsEntity?) -> Unit = {}
|
||||||
|
) {
|
||||||
|
pendingGoodsList = goodsList
|
||||||
|
val intent = Intent(activity, FoodRecognizeActivity::class.java).apply {
|
||||||
|
putExtra(EXTRA_IMAGE_URI, imageUri)
|
||||||
|
}
|
||||||
|
activity.startActivity(intent) { resultIntent ->
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
val item = resultIntent?.getSerializableExtra(EXTRA_SELECTED_ITEM) as? CookFoodGoodsEntity
|
||||||
|
onResult(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭当前识别页面,并将选中食材通过 Activity Result 回传
|
||||||
|
* 由外部(如重量归零时)调用
|
||||||
|
*/
|
||||||
|
fun close() {
|
||||||
|
ActivityManager.getActivityStack()
|
||||||
|
.filterIsInstance<FoodRecognizeActivity>()
|
||||||
|
.lastOrNull()
|
||||||
|
?.finishWithResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityFoodRecognizeBinding
|
||||||
|
|
||||||
|
private val list = mutableListOf<CookFoodGoodsEntity>()
|
||||||
|
|
||||||
|
private val adapter = GoodsInfoSearchAdapter(list).apply {
|
||||||
|
setOnItemClickListener { _, _, position ->
|
||||||
|
// 更新选中状态
|
||||||
|
list.forEachIndexed { i, item -> item.isClicked = (i == position) }
|
||||||
|
notifyDataSetChanged()
|
||||||
|
// 同步更新选中食材名称
|
||||||
|
binding.tvSelectedFood.text = list[position].goodsName ?: "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = ActivityFoodRecognizeBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
setHeaderBackground()
|
||||||
|
|
||||||
|
// 显示标题栏
|
||||||
|
setTitleBar(titleBarAction = { it.visible() }, titleAction = { it.text = "食材识别" })
|
||||||
|
|
||||||
|
initData()
|
||||||
|
initRecyclerView()
|
||||||
|
initClickListeners()
|
||||||
|
registerWeightListener()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Intent 读取数据并初始化页面
|
||||||
|
*/
|
||||||
|
private fun initData() {
|
||||||
|
// 加载食材照片
|
||||||
|
val imageUri = intent.getStringExtra(EXTRA_IMAGE_URI)
|
||||||
|
if (!imageUri.isNullOrBlank()) {
|
||||||
|
binding.ivFoodPhoto.setImageURI(Uri.parse(imageUri))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取出内存缓存的食材列表,取用后立即清空
|
||||||
|
val goodsList = pendingGoodsList?.also { pendingGoodsList = null }
|
||||||
|
if (!goodsList.isNullOrEmpty()) {
|
||||||
|
list.addAll(goodsList)
|
||||||
|
// 默认选中第一项
|
||||||
|
list[0].isClicked = true
|
||||||
|
binding.tvSelectedFood.text = list[0].goodsName ?: "-"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化 RecyclerView
|
||||||
|
*/
|
||||||
|
private fun initRecyclerView() {
|
||||||
|
binding.recyclerView.run {
|
||||||
|
layoutManager = GridLayoutManager(this@FoodRecognizeActivity, 2)
|
||||||
|
adapter = this@FoodRecognizeActivity.adapter
|
||||||
|
}
|
||||||
|
updateListVisibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据列表是否有数据控制 RecyclerView 显隐
|
||||||
|
*/
|
||||||
|
private fun updateListVisibility() {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
binding.recyclerView.gone()
|
||||||
|
} else {
|
||||||
|
binding.recyclerView.visible()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化按钮点击事件
|
||||||
|
*/
|
||||||
|
private fun initClickListeners() {
|
||||||
|
// 更换食材:打开食材搜索弹窗
|
||||||
|
binding.btnChangeFood.clickWithDebounce {
|
||||||
|
val currentName = if (list.isNotEmpty()) list.firstOrNull { it.isClicked }?.goodsName else null
|
||||||
|
FoodSearchDialog(
|
||||||
|
activity = this,
|
||||||
|
defGoodsName = currentName,
|
||||||
|
onItemSelected = { item ->
|
||||||
|
// 用搜索结果替换当前选中食材
|
||||||
|
updateSelectedFood(item)
|
||||||
|
}
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消:关闭页面
|
||||||
|
binding.btnCancel.setOnClickListener { finish() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用新食材替换当前选中项(或追加到列表首位)
|
||||||
|
*/
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
private fun updateSelectedFood(item: CookFoodGoodsEntity) {
|
||||||
|
item.isClicked = true
|
||||||
|
val clickedIndex = list.indexOfFirst { it.isClicked && it.goodsId != item.goodsId }
|
||||||
|
if (clickedIndex >= 0) {
|
||||||
|
list[clickedIndex].isClicked = false
|
||||||
|
}
|
||||||
|
// 若列表中已存在则直接选中,否则插入到首位
|
||||||
|
val existIndex = list.indexOfFirst { it.goodsId == item.goodsId }
|
||||||
|
if (existIndex >= 0) {
|
||||||
|
list.forEachIndexed { i, e -> e.isClicked = (i == existIndex) }
|
||||||
|
} else {
|
||||||
|
list.add(0, item)
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
binding.tvSelectedFood.text = item.goodsName ?: "-"
|
||||||
|
updateListVisibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册秤1重量监听
|
||||||
|
*/
|
||||||
|
private fun registerWeightListener() {
|
||||||
|
WeightUtil.addWeightListener(TAG) { address, _, weight ->
|
||||||
|
if (address == AddressUtil.ONE) {
|
||||||
|
runOnUiThread {
|
||||||
|
binding.tvWeight.text = "$weight g"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 携带当前选中食材关闭页面
|
||||||
|
*/
|
||||||
|
private fun finishWithResult() {
|
||||||
|
val selected = list.firstOrNull { it.isClicked }
|
||||||
|
val intent = Intent().apply {
|
||||||
|
selected?.let { putExtra(EXTRA_SELECTED_ITEM, it) }
|
||||||
|
}
|
||||||
|
setResult(RESULT_OK, intent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
WeightUtil.removeWeightListener(TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,37 +12,31 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.chad.library.adapter4.BaseMultiItemAdapter
|
import com.chad.library.adapter4.BaseMultiItemAdapter
|
||||||
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.AlignItems
|
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.R
|
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.base.BaseActivity
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
import com.shuwei.dish.match.databinding.ActivityMasterScaleBinding
|
import com.shuwei.dish.match.databinding.ActivityMasterScaleBinding
|
||||||
import com.shuwei.dish.match.databinding.ListItemScale22RowBinding
|
|
||||||
import com.shuwei.dish.match.databinding.ListTypeScale1Binding
|
import com.shuwei.dish.match.databinding.ListTypeScale1Binding
|
||||||
import com.shuwei.dish.match.databinding.ListTypeScale18Binding
|
import com.shuwei.dish.match.databinding.ListTypeScale18Binding
|
||||||
import com.shuwei.dish.match.databinding.ListTypeScale2Binding
|
import com.shuwei.dish.match.databinding.ListTypeScale2Binding
|
||||||
import com.shuwei.dish.match.databinding.ListTypeScale22Binding
|
import com.shuwei.dish.match.databinding.ListTypeScale22Binding
|
||||||
import com.shuwei.dish.match.scale.ScaleData
|
import com.shuwei.dish.match.scale.ScaleData
|
||||||
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
import com.shuwei.dish.match.scale.ScaleServiceManager
|
import com.shuwei.dish.match.scale.ScaleServiceManager
|
||||||
import com.shuwei.dish.match.utils.NetworkUtil
|
import com.shuwei.dish.match.utils.NetworkUtil
|
||||||
import com.shuwei.dish.match.utils.WeightUtil
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
|
import com.shuwei.dish.match.utils.ext.toast
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private const val DEVICE_ID_2 = "8fc2ab34-2137-3112-acca-f884ea8736d4"
|
|
||||||
private const val DEVICE_ID_22 = "a46fa55c-113c-3511-bb1f-41e5eff77c4b"
|
|
||||||
private const val DEVICE_ID_18 = "1038da9f-c6eb-326e-a1d9-d6d3af978b22"
|
|
||||||
private const val DEVICE_ID_1 = "7cc0f6ea-f13d-3013-a867-fc998eb554ac"
|
|
||||||
|
|
||||||
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)
|
|
||||||
private val SCALE_ORDER_18 = listOf(6, 5, 4, 3, 2, 1, 12, 11, 10, 9, 8, 7, 18, 17, 16, 15, 14, 13)
|
|
||||||
private val deviceOrder = listOf(DEVICE_ID_2, DEVICE_ID_22, DEVICE_ID_18, DEVICE_ID_1)
|
|
||||||
|
|
||||||
private const val VIEW_TYPE_SCALE2 = 0
|
private const val VIEW_TYPE_SCALE2 = 0
|
||||||
private const val VIEW_TYPE_SCALE22 = 1
|
private const val VIEW_TYPE_SCALE22 = 1
|
||||||
private const val VIEW_TYPE_SCALE18 = 2
|
private const val VIEW_TYPE_SCALE18 = 2
|
||||||
@@ -89,7 +83,7 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
.groupBy { it.deviceId }
|
.groupBy { it.deviceId }
|
||||||
.entries
|
.entries
|
||||||
.sortedWith(compareBy { entry ->
|
.sortedWith(compareBy { entry ->
|
||||||
deviceOrder.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) ->
|
.map { (deviceId, scales) ->
|
||||||
DeviceGroup(
|
DeviceGroup(
|
||||||
@@ -99,17 +93,43 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
scales = scales.sortedBy { it.address }
|
scales = scales.sortedBy { it.address }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
updateList(groups)
|
|
||||||
|
val structureChanged = groups.size != groupList.size ||
|
||||||
|
groups.indices.any { groups[it].deviceId != groupList[it].deviceId }
|
||||||
|
|
||||||
|
if (structureChanged) {
|
||||||
|
updateList(groups)
|
||||||
|
} else {
|
||||||
|
groups.forEach { group -> adapter.updateScalesForDevice(group.deviceId, group.scales) }
|
||||||
|
}
|
||||||
|
|
||||||
updateStatusBar(scaleMap)
|
updateStatusBar(scaleMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
|
||||||
private fun updateList(groups: List<DeviceGroup>) {
|
private fun updateList(groups: List<DeviceGroup>) {
|
||||||
groupList.clear()
|
if (groupList.isEmpty()) {
|
||||||
groupList.addAll(groups)
|
groupList.addAll(groups)
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 精确 diff,只 notify 真正变化的 item,避免全量刷新打断触摸事件
|
||||||
|
groups.forEachIndexed { i, group ->
|
||||||
|
if (i < groupList.size) {
|
||||||
|
if (groupList[i] != group) {
|
||||||
|
groupList[i] = group
|
||||||
|
adapter.notifyItemChanged(i)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
groupList.add(group)
|
||||||
|
adapter.notifyItemInserted(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (groupList.size > groups.size) {
|
||||||
|
groupList.removeAt(groupList.lastIndex)
|
||||||
|
adapter.notifyItemRemoved(groupList.size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
@@ -122,6 +142,18 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
private inner class DeviceGroupAdapter(data: MutableList<DeviceGroup>) :
|
private inner class DeviceGroupAdapter(data: MutableList<DeviceGroup>) :
|
||||||
BaseMultiItemAdapter<DeviceGroup>(data) {
|
BaseMultiItemAdapter<DeviceGroup>(data) {
|
||||||
|
|
||||||
|
private val scale1VHMap = mutableMapOf<String, Scale1VH>()
|
||||||
|
private val scale2VHMap = mutableMapOf<String, Scale2VH>()
|
||||||
|
private val scale22VHMap = mutableMapOf<String, Scale22VH>()
|
||||||
|
private val scale18VHMap = mutableMapOf<String, Scale18VH>()
|
||||||
|
|
||||||
|
fun updateScalesForDevice(deviceId: String, scales: List<ScaleData>) {
|
||||||
|
scale1VHMap[deviceId]?.updateScales(scales)
|
||||||
|
scale2VHMap[deviceId]?.updateScales(scales)
|
||||||
|
scale22VHMap[deviceId]?.updateScales(scales)
|
||||||
|
scale18VHMap[deviceId]?.updateScales(scales)
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
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) =
|
override fun onCreate(context: Context, parent: ViewGroup, viewType: Int) =
|
||||||
@@ -129,7 +161,9 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
||||||
item ?: return
|
item ?: return
|
||||||
val b = ListTypeScale2Binding.bind(holder.itemView)
|
val b = ListTypeScale2Binding.bind(holder.itemView)
|
||||||
Scale2VH(b).bind(item)
|
val vh = Scale2VH(b)
|
||||||
|
scale2VHMap[item.deviceId] = vh
|
||||||
|
vh.bind(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
addItemType(VIEW_TYPE_SCALE22, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
|
addItemType(VIEW_TYPE_SCALE22, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
|
||||||
@@ -138,7 +172,10 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
||||||
item ?: return
|
item ?: return
|
||||||
val b = ListTypeScale22Binding.bind(holder.itemView)
|
val b = ListTypeScale22Binding.bind(holder.itemView)
|
||||||
Scale22VH(b).bind(item)
|
// 每次 onBind 时注册/更新 VH 缓存,后续数据更新直接走 VH,不再触发 notify
|
||||||
|
val vh = Scale22VH(b)
|
||||||
|
scale22VHMap[item.deviceId] = vh
|
||||||
|
vh.bind(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
addItemType(VIEW_TYPE_SCALE18, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
|
addItemType(VIEW_TYPE_SCALE18, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
|
||||||
@@ -147,7 +184,9 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
||||||
item ?: return
|
item ?: return
|
||||||
val b = ListTypeScale18Binding.bind(holder.itemView)
|
val b = ListTypeScale18Binding.bind(holder.itemView)
|
||||||
Scale18VH(b).bind(item)
|
val vh = Scale18VH(b)
|
||||||
|
scale18VHMap[item.deviceId] = vh
|
||||||
|
vh.bind(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
addItemType(VIEW_TYPE_SCALE1, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
|
addItemType(VIEW_TYPE_SCALE1, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
|
||||||
@@ -156,13 +195,15 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
||||||
item ?: return
|
item ?: return
|
||||||
val b = ListTypeScale1Binding.bind(holder.itemView)
|
val b = ListTypeScale1Binding.bind(holder.itemView)
|
||||||
Scale1VH(b).bind(item)
|
val vh = Scale1VH(b)
|
||||||
|
scale1VHMap[item.deviceId] = vh
|
||||||
|
vh.bind(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
onItemViewType { position, list -> when (list.getOrNull(position)?.deviceId) {
|
onItemViewType { position, list -> when (list.getOrNull(position)?.deviceId) {
|
||||||
DEVICE_ID_2 -> VIEW_TYPE_SCALE2
|
ScaleDeviceConfig.DEVICE_ID_2 -> VIEW_TYPE_SCALE2
|
||||||
DEVICE_ID_22 -> VIEW_TYPE_SCALE22
|
ScaleDeviceConfig.DEVICE_ID_22 -> VIEW_TYPE_SCALE22
|
||||||
DEVICE_ID_18 -> VIEW_TYPE_SCALE18
|
ScaleDeviceConfig.DEVICE_ID_18 -> VIEW_TYPE_SCALE18
|
||||||
else -> VIEW_TYPE_SCALE1
|
else -> VIEW_TYPE_SCALE1
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
@@ -171,14 +212,36 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
// ── ViewHolder: 2个秤 ──────────────────────────────────────────────────
|
// ── ViewHolder: 2个秤 ──────────────────────────────────────────────────
|
||||||
|
|
||||||
private inner class Scale2VH(val b: ListTypeScale2Binding) : RecyclerView.ViewHolder(b.root) {
|
private inner class Scale2VH(val b: ListTypeScale2Binding) : RecyclerView.ViewHolder(b.root) {
|
||||||
|
private lateinit var cells: List<LinearLayout>
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun bind(group: DeviceGroup) {
|
fun bind(group: DeviceGroup) {
|
||||||
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
||||||
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
||||||
b.llScaleContainer.removeAllViews()
|
|
||||||
val cellSize = (resources.displayMetrics.widthPixels - 4 * 4.dp - 10.dp) / 2
|
if (!::cells.isInitialized) {
|
||||||
group.scales.take(2).forEach { scale ->
|
val cellSize = (resources.displayMetrics.widthPixels - 4 * 4.dp - 10.dp) / 2
|
||||||
b.llScaleContainer.addView(makeScaleCell(scale, cellSize))
|
cells = group.scales.take(2).map { scale ->
|
||||||
|
makeScaleCell(scale, cellSize).also { cell ->
|
||||||
|
cell.setOnClickListener {
|
||||||
|
val s = cell.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,17 +249,16 @@ 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 bound = false
|
private var innerAdapter: Scale22GridAdapter? = null
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun bind(group: DeviceGroup) {
|
fun bind(group: DeviceGroup) {
|
||||||
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
||||||
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
||||||
|
|
||||||
if (!bound) {
|
if (innerAdapter == null) {
|
||||||
// 计算格子尺寸(与 Scale22GridActivity 一致)
|
|
||||||
val screenWidth = resources.displayMetrics.widthPixels
|
val screenWidth = resources.displayMetrics.widthPixels
|
||||||
val usable = screenWidth - 8 * 4.dp - 10.dp // 减去8列格子margin + 外层RV左右margin各5dp
|
val usable = screenWidth - 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
|
||||||
val rvHeight = smallSize * 3 + 3 * 4.dp
|
val rvHeight = smallSize * 3 + 3 * 4.dp
|
||||||
@@ -207,154 +269,83 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
alignItems = AlignItems.FLEX_START
|
alignItems = AlignItems.FLEX_START
|
||||||
}
|
}
|
||||||
b.rvScales.itemAnimator = null
|
b.rvScales.itemAnimator = null
|
||||||
b.rvScales.adapter = Scale22InnerAdapter(largeSize, smallSize)
|
innerAdapter = Scale22GridAdapter(largeSize, smallSize).apply {
|
||||||
bound = true
|
onItemClick = { scale ->
|
||||||
|
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.rvScales.adapter = innerAdapter
|
||||||
}
|
}
|
||||||
val scaleByAddress = group.scales.associateBy { it.address }
|
updateScales(group.scales)
|
||||||
val ordered = SCALE_ORDER_22.map { scaleByAddress[it] }
|
}
|
||||||
(b.rvScales.adapter as Scale22InnerAdapter).update(ordered)
|
|
||||||
|
/** 仅更新秤数据,不触发外层任何 notify */
|
||||||
|
fun updateScales(scales: List<ScaleData>) {
|
||||||
|
innerAdapter?.updateByAddress(scales)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 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 bound = false
|
private var innerAdapter: Scale18GridAdapter? = null
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun bind(group: DeviceGroup) {
|
fun bind(group: DeviceGroup) {
|
||||||
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
||||||
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
||||||
|
|
||||||
if (!bound) {
|
if (innerAdapter == null) {
|
||||||
val gridLayoutManager = GridLayoutManager(b.root.context, 6).apply {
|
val gridLayoutManager = GridLayoutManager(b.root.context, 6).apply {
|
||||||
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||||
// 每个格子占1 span,共6列
|
|
||||||
override fun getSpanSize(position: Int) = 1
|
override fun getSpanSize(position: Int) = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.rvScales.layoutManager = gridLayoutManager
|
b.rvScales.layoutManager = gridLayoutManager
|
||||||
b.rvScales.itemAnimator = null
|
b.rvScales.itemAnimator = null
|
||||||
b.rvScales.adapter = Scale18InnerAdapter()
|
innerAdapter = Scale18GridAdapter().apply {
|
||||||
bound = true
|
onItemClick = { scale ->
|
||||||
|
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.rvScales.adapter = innerAdapter
|
||||||
}
|
}
|
||||||
val scaleByAddress = group.scales.associateBy { it.address }
|
updateScales(group.scales)
|
||||||
val ordered = SCALE_ORDER_18.map { scaleByAddress[it] }
|
}
|
||||||
(b.rvScales.adapter as Scale18InnerAdapter).update(ordered)
|
|
||||||
|
/** 仅更新秤数据,不触发外层任何 notify */
|
||||||
|
fun updateScales(scales: List<ScaleData>) {
|
||||||
|
innerAdapter?.updateByAddress(scales)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── ViewHolder: 1个秤 ──────────────────────────────────────────────────
|
// ── ViewHolder: 1个秤 ──────────────────────────────────────────────────
|
||||||
|
|
||||||
private inner class Scale1VH(val b: ListTypeScale1Binding) : RecyclerView.ViewHolder(b.root) {
|
private inner class Scale1VH(val b: ListTypeScale1Binding) : RecyclerView.ViewHolder(b.root) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
b.llScaleContainer.setOnClickListener {
|
||||||
|
val scale = b.llScaleContainer.tag as? ScaleData ?: return@setOnClickListener
|
||||||
|
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun bind(group: DeviceGroup) {
|
fun bind(group: DeviceGroup) {
|
||||||
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
||||||
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
||||||
val scale = group.scales.firstOrNull()
|
updateScales(group.scales)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateScales(scales: List<ScaleData>) {
|
||||||
|
val scale = scales.firstOrNull()
|
||||||
|
b.llScaleContainer.tag = scale
|
||||||
b.tvAddr.text = scale?.let { "秤 ${it.address}" } ?: ""
|
b.tvAddr.text = scale?.let { "秤 ${it.address}" } ?: ""
|
||||||
b.tvWeight.text = scale?.let { "${it.weight} g" } ?: ""
|
b.tvWeight.text = scale?.let { "${it.weight} g" } ?: ""
|
||||||
b.tvState.text = stateStr(scale?.state)
|
b.tvState.text = stateStr(scale?.state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 22格内嵌 Adapter ───────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private inner class Scale22InnerAdapter(
|
|
||||||
private val largeSize: Int,
|
|
||||||
private val smallSize: Int
|
|
||||||
) : BaseQuickAdapter<ScaleData, Scale22InnerAdapter.VH>(mutableListOf()) {
|
|
||||||
|
|
||||||
inner class VH(val b: ListItemScale22RowBinding) : QuickViewHolder(b.root)
|
|
||||||
|
|
||||||
// 用 address=0 的哨兵表示空位
|
|
||||||
private val empty = ScaleData("", 0, 0.0, -1, 0)
|
|
||||||
|
|
||||||
fun update(newList: List<ScaleData?>) {
|
|
||||||
val filled = newList.map { it ?: empty }
|
|
||||||
if (items.isEmpty()) {
|
|
||||||
submitList(filled)
|
|
||||||
} else {
|
|
||||||
filled.forEachIndexed { i, scale ->
|
|
||||||
if (items.getOrNull(i) != scale) set(i, scale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
return VH(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 18格内嵌 Adapter ───────────────────────────────────────────────────
|
|
||||||
|
|
||||||
private inner class Scale18InnerAdapter : BaseQuickAdapter<ScaleData, Scale18InnerAdapter.VH>(mutableListOf()) {
|
|
||||||
|
|
||||||
inner class VH(val b: ListItemScale22RowBinding) : QuickViewHolder(b.root)
|
|
||||||
|
|
||||||
private val empty = ScaleData("", 0, 0.0, -1, 0)
|
|
||||||
|
|
||||||
fun update(newList: List<ScaleData?>) {
|
|
||||||
val filled = newList.map { it ?: empty }
|
|
||||||
if (items.isEmpty()) {
|
|
||||||
submitList(filled)
|
|
||||||
} else {
|
|
||||||
filled.forEachIndexed { i, scale ->
|
|
||||||
if (items.getOrNull(i) != scale) set(i, scale)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
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)
|
|
||||||
return VH(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── 工具方法 ───────────────────────────────────────────────────────────
|
// ── 工具方法 ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private fun stateStr(state: Int?) = when (state) {
|
private fun stateStr(state: Int?) = when (state) {
|
||||||
@@ -365,7 +356,7 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
else -> "$state"
|
else -> "$state"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 创建正方形秤格子(用于 Scale2VH) */
|
/** 创建正方形秤格子(用于 Scale2VH 初始化) */
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
private fun makeScaleCell(scale: ScaleData, cellSize: Int): LinearLayout {
|
private fun makeScaleCell(scale: ScaleData, cellSize: Int): LinearLayout {
|
||||||
val margin = 2.dp
|
val margin = 2.dp
|
||||||
@@ -380,9 +371,18 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
addView(makeCellText("秤${scale.address}", (baseSize * 0.16f).coerceIn(9f, 14f).toInt()))
|
addView(makeCellText("秤${scale.address}", (baseSize * 0.16f).coerceIn(9f, 14f).toInt()))
|
||||||
addView(makeCellText("${scale.weight}g", (baseSize * 0.22f).coerceIn(12f, 20f).toInt(), bold = true))
|
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()))
|
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 = "${scale.weight}g"
|
||||||
|
(cell.getChildAt(2) as? TextView)?.text = stateStr(scale.state)
|
||||||
|
}
|
||||||
|
|
||||||
private fun makeCellText(text: String, spSize: Int, bold: Boolean = false): TextView =
|
private fun makeCellText(text: String, spSize: Int, bold: Boolean = false): TextView =
|
||||||
TextView(this).apply {
|
TextView(this).apply {
|
||||||
this.text = text
|
this.text = text
|
||||||
@@ -395,4 +395,23 @@ class MasterScaleActivity : BaseActivity() {
|
|||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
includeFontPadding = false
|
includeFontPadding = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹出清零确认对话框
|
||||||
|
* @param deviceId 目标设备 ID
|
||||||
|
* @param ip 设备 IP
|
||||||
|
* @param address 目标秤地址
|
||||||
|
*/
|
||||||
|
private fun showTareDialog(deviceId: String, ip: String, address: Int) {
|
||||||
|
CommonDialog(this)
|
||||||
|
.setTitle("清零确认")
|
||||||
|
.setContent("设备 IP:${ip.ifEmpty { "未知" }}\n确认对秤 $address 执行清零操作?")
|
||||||
|
.setNegativeButton("取消")
|
||||||
|
.setPositiveButton("确认") {
|
||||||
|
val sent = ScaleServiceManager.sendTare(deviceId, address)
|
||||||
|
if (!sent) toast("设备未连接,清零失败")
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import com.shuwei.dish.match.R
|
|||||||
import com.shuwei.dish.match.adapter.DishPartAdapter
|
import com.shuwei.dish.match.adapter.DishPartAdapter
|
||||||
import com.shuwei.dish.match.base.BaseActivity
|
import com.shuwei.dish.match.base.BaseActivity
|
||||||
import com.shuwei.dish.match.databinding.ActivityPrepareCookBinding
|
import com.shuwei.dish.match.databinding.ActivityPrepareCookBinding
|
||||||
import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding
|
|
||||||
import com.shuwei.dish.match.dialog.CommonDialog
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
import com.shuwei.dish.match.dialog.FoodRecognizeDialog
|
|
||||||
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
||||||
import com.shuwei.dish.match.entity.CookFoodEntity
|
import com.shuwei.dish.match.entity.CookFoodEntity
|
||||||
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
||||||
@@ -159,6 +157,10 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val isTakingPhoto = AtomicBoolean(false)
|
private val isTakingPhoto = AtomicBoolean(false)
|
||||||
|
private var isRecognizeOpen = false
|
||||||
|
// 页面可见时允许识别,onPause 后禁止,onResume 后恢复
|
||||||
|
private var isRecognizeEnabled = true
|
||||||
|
private var lastPhotoUri: Uri? = null
|
||||||
private var lastWeight = 0.0
|
private var lastWeight = 0.0
|
||||||
private var currentWeight = 0.0
|
private var currentWeight = 0.0
|
||||||
|
|
||||||
@@ -169,10 +171,11 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
private fun recognizeFood(weight: Double) {
|
private fun recognizeFood(weight: Double) {
|
||||||
this.currentWeight = weight
|
this.currentWeight = weight
|
||||||
if (weight < WEIGHT_RECOGNIZE_VALUE) {
|
if (weight < WEIGHT_RECOGNIZE_VALUE) {
|
||||||
if (recognizeDialog.isShowing) recognizeDialog.dismiss()
|
isRecognizeOpen = false
|
||||||
|
FoodRecognizeActivity.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isTakingPhoto.get() || recognizeDialog.isShowing || abs(lastWeight - weight) <= WEIGHT_CHANGE_VALUE) return
|
if (!isRecognizeEnabled || isTakingPhoto.get() || isRecognizeOpen || abs(lastWeight - weight) <= WEIGHT_CHANGE_VALUE) return
|
||||||
isTakingPhoto.set(true)
|
isTakingPhoto.set(true)
|
||||||
cameraUtils.takePhoto(
|
cameraUtils.takePhoto(
|
||||||
succCallback = cameraSuccessCallback,
|
succCallback = cameraSuccessCallback,
|
||||||
@@ -195,9 +198,13 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
// materialType =
|
// materialType =
|
||||||
isNewDishType = true
|
isNewDishType = true
|
||||||
isOriginalData = false
|
isOriginalData = false
|
||||||
|
useWeight = lastWeight
|
||||||
|
isSetFinished = true
|
||||||
})
|
})
|
||||||
onFoodItemClick(list.size - 1)
|
val positon = list.size - 1
|
||||||
binding.rvDishPartList.smoothScrollToPosition(list.size - 1)
|
dishPartAdapter.notifyItemInserted(positon)
|
||||||
|
onFoodItemClick(positon)
|
||||||
|
binding.rvDishPartList.smoothScrollToPosition(positon)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -239,18 +246,12 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showRemindDialog() {
|
private fun showRemindDialog() {
|
||||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
CommonDialog(this)
|
||||||
CommonDialog(this).apply {
|
.setTitle("温馨提示")
|
||||||
messageView = remindBinding.root
|
.setNegativeButton("返回调整")
|
||||||
leftText = "返回调整"
|
.setPositiveButton("确认无误") { openSubmitPage() }
|
||||||
rightText = "确认无误"
|
.setOnDismissCallback { hideStatusBar() }
|
||||||
onLeftClick = { dismiss() }
|
.show()
|
||||||
onRightClick = {
|
|
||||||
openSubmitPage()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
onDismiss = { hideStatusBar() }
|
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -305,18 +306,24 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
|
|
||||||
private var firstReqSize = 0
|
private var firstReqSize = 0
|
||||||
private val list = mutableListOf<CookFoodGoodsEntity>()
|
private val list = mutableListOf<CookFoodGoodsEntity>()
|
||||||
private val recognizeDialog by lazy {
|
|
||||||
FoodRecognizeDialog(
|
|
||||||
activity = this,
|
|
||||||
onDismiss = { isTakingPhoto.set(false) },
|
|
||||||
onItemSelected = foodSelectCallback
|
|
||||||
)
|
|
||||||
}
|
|
||||||
private val dishPartAdapter by lazy {
|
private val dishPartAdapter by lazy {
|
||||||
DishPartAdapter(list).apply {
|
DishPartAdapter(list).apply {
|
||||||
setOnItemClickListener { _, _, positon ->
|
setOnItemClickListener { _, _, positon ->
|
||||||
onFoodItemClick(positon)
|
onFoodItemClick(positon)
|
||||||
}
|
}
|
||||||
|
addOnItemChildClickListener(R.id.ivClearIcon) { _, _, positon ->
|
||||||
|
if (list[positon].isOriginalData) {
|
||||||
|
list[positon].run {
|
||||||
|
isNewDishType = false
|
||||||
|
isOriginalData = false
|
||||||
|
useWeight = 0.0
|
||||||
|
isSetFinished = false
|
||||||
|
}
|
||||||
|
notifyItemChanged(positon)
|
||||||
|
return@addOnItemChildClickListener
|
||||||
|
}
|
||||||
|
removeAt(positon)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,11 +402,14 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
isRecognizeEnabled = true
|
||||||
|
isRecognizeOpen = false
|
||||||
cameraUtils.bind()
|
cameraUtils.bind()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
|
isRecognizeEnabled = false
|
||||||
cameraUtils.unbind()
|
cameraUtils.unbind()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,20 +441,13 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
* 未保存数据提醒
|
* 未保存数据提醒
|
||||||
*/
|
*/
|
||||||
private fun saveDataRemindDialog() {
|
private fun saveDataRemindDialog() {
|
||||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
CommonDialog(this)
|
||||||
remindBinding.tvDialogTitle.text = "温馨提示"
|
.setTitle("温馨提示")
|
||||||
remindBinding.tvDialogContent.text = "您好,当前页面存在尚未保存的数据,确认返回上页吗?"
|
.setContent("您好,当前页面存在尚未保存的数据,确认返回吗?")
|
||||||
CommonDialog(this).apply {
|
.setNegativeButton("取消")
|
||||||
messageView = remindBinding.root
|
.setPositiveButton("确认") { finish() }
|
||||||
leftText = "取消"
|
.setOnDismissCallback { hideStatusBar() }
|
||||||
rightText = "确认"
|
.show()
|
||||||
onLeftClick = { dismiss() }
|
|
||||||
onRightClick = {
|
|
||||||
dismiss()
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
onDismiss = { hideStatusBar() }
|
|
||||||
}.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -452,6 +455,7 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
*/
|
*/
|
||||||
private val cameraSuccessCallback: (Uri) -> Unit = { uri ->
|
private val cameraSuccessCallback: (Uri) -> Unit = { uri ->
|
||||||
Log.d(TAG, "takePhoto success")
|
Log.d(TAG, "takePhoto success")
|
||||||
|
lastPhotoUri = uri
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val bitmap = ImageUtil.uriToBitmap(this@PrepareCookActivity, uri)
|
val bitmap = ImageUtil.uriToBitmap(this@PrepareCookActivity, uri)
|
||||||
@@ -499,7 +503,13 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
foodId = index.toString()
|
foodId = index.toString()
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
recognizeDialog.loadData(foodList)
|
FoodRecognizeActivity.start(
|
||||||
|
activity = this,
|
||||||
|
imageUri = lastPhotoUri?.toString(),
|
||||||
|
goodsList = ArrayList(foodList),
|
||||||
|
onResult = { item -> item?.let { foodSelectCallback(it) } }
|
||||||
|
)
|
||||||
|
isRecognizeOpen = true
|
||||||
isTakingPhoto.set(false)
|
isTakingPhoto.set(false)
|
||||||
lastWeight = currentWeight
|
lastWeight = currentWeight
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import com.shuwei.dish.match.base.BaseApp
|
|||||||
import com.shuwei.dish.match.databinding.ActivitySamplingBinding
|
import com.shuwei.dish.match.databinding.ActivitySamplingBinding
|
||||||
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding
|
||||||
import com.shuwei.dish.match.db.AppRepository
|
import com.shuwei.dish.match.db.AppRepository
|
||||||
import com.shuwei.dish.match.dialog.DialogTool
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
import com.shuwei.dish.match.entity.CookFoodEntity
|
import com.shuwei.dish.match.entity.CookFoodEntity
|
||||||
import com.shuwei.dish.match.entity.FoodRecord
|
import com.shuwei.dish.match.entity.FoodRecord
|
||||||
import com.shuwei.dish.match.utils.DateTimeUtil
|
import com.shuwei.dish.match.utils.DateTimeUtil
|
||||||
@@ -378,16 +378,12 @@ class SamplingListActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showDeviceConfigDialog() {
|
private fun showDeviceConfigDialog() {
|
||||||
DialogTool.load(
|
CommonDialog(this)
|
||||||
context = this,
|
.setTitle("温馨提示")
|
||||||
title = "温馨提示",
|
.setContent("请先在设备配置页面进行调料设置")
|
||||||
content = "请先在设备配置页面进行调料设置",
|
.setNegativeButton("取消")
|
||||||
leftBtnText = "取消",
|
.setPositiveButton("去设置") { startActivity<SettingActivity>() }
|
||||||
rightBtnText = "去设置",
|
.show()
|
||||||
leftBtnClick = {},
|
|
||||||
rightBtnClick = {
|
|
||||||
startActivity<SettingActivity>()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isVisible = false
|
private var isVisible = false
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import com.shuwei.dish.match.base.BaseActivity
|
|||||||
import com.shuwei.dish.match.base.BaseApp
|
import com.shuwei.dish.match.base.BaseApp
|
||||||
import com.shuwei.dish.match.databinding.ActivitySelectDishBinding
|
import com.shuwei.dish.match.databinding.ActivitySelectDishBinding
|
||||||
import com.shuwei.dish.match.db.AppRepository
|
import com.shuwei.dish.match.db.AppRepository
|
||||||
import com.shuwei.dish.match.dialog.DialogTool
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
import com.shuwei.dish.match.entity.CookFoodEntity
|
import com.shuwei.dish.match.entity.CookFoodEntity
|
||||||
import com.shuwei.dish.match.entity.FoodRecord
|
import com.shuwei.dish.match.entity.FoodRecord
|
||||||
import com.shuwei.dish.match.ui.fragment.DishListFragment
|
import com.shuwei.dish.match.ui.fragment.DishListFragment
|
||||||
@@ -95,17 +95,12 @@ class SelectDishActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showDeviceConfigDialog() {
|
private fun showDeviceConfigDialog() {
|
||||||
DialogTool.load(
|
CommonDialog(this)
|
||||||
context = this,
|
.setTitle("温馨提示")
|
||||||
title = "温馨提示",
|
.setContent("请先在设备配置页面进行调料设置")
|
||||||
content = "请先在设备配置页面进行调料设置",
|
.setNegativeButton("取消")
|
||||||
leftBtnText = "取消",
|
.setPositiveButton("去设置") { startActivity<SettingActivity>() }
|
||||||
rightBtnText = "去设置",
|
.show()
|
||||||
leftBtnClick = {},
|
|
||||||
rightBtnClick = {
|
|
||||||
startActivity<SettingActivity>()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addViewListener() {
|
fun addViewListener() {
|
||||||
|
|||||||
@@ -2,33 +2,45 @@ package com.shuwei.dish.match.ui
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import androidx.core.view.updateLayoutParams
|
||||||
import android.view.ViewGroup
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import android.widget.LinearLayout
|
||||||
import androidx.activity.addCallback
|
import androidx.activity.addCallback
|
||||||
|
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.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
|
||||||
import com.shuwei.dish.match.databinding.ActivitySlaveBinding
|
import com.shuwei.dish.match.databinding.ActivitySlaveBinding
|
||||||
import com.shuwei.dish.match.databinding.ListItemScaleRowBinding
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
|
import com.shuwei.dish.match.scale.ScaleData
|
||||||
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
import com.shuwei.dish.match.scale.ScaleServiceManager
|
import com.shuwei.dish.match.scale.ScaleServiceManager
|
||||||
import com.shuwei.dish.match.utils.NetworkUtil
|
import com.shuwei.dish.match.utils.NetworkUtil
|
||||||
import com.shuwei.dish.match.utils.WeightUtil
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 子设备专属页面(小屏幕设备使用)
|
* 子设备专属页面(小屏幕设备使用)
|
||||||
* 仅展示本机秤的实时数据和主设备连接状态
|
* 仅展示本机秤的实时数据和主设备连接状态
|
||||||
* 布局简洁,适配小屏幕
|
* 22个秤和18个秤使用与 MasterScaleActivity 相同的网格布局,其余使用线性列表
|
||||||
*/
|
*/
|
||||||
class SlaveActivity : BaseActivity() {
|
class SlaveActivity : BaseActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivitySlaveBinding
|
private lateinit var binding: ActivitySlaveBinding
|
||||||
|
|
||||||
/** 本机秤数据列表:address → (weight, state) */
|
/** 本机秤数据列表:线性列表模式使用 */
|
||||||
private data class ScaleItem(val address: Int, val weight: Double, val state: Int)
|
private val linearAdapter = ScaleRowAdapter()
|
||||||
|
|
||||||
private val scaleList = mutableListOf<ScaleItem>()
|
/** 22个秤网格 adapter */
|
||||||
private val adapter = ScaleAdapter(scaleList)
|
private var scale22Adapter: Scale22GridAdapter? = null
|
||||||
|
/** 18个秤网格 adapter */
|
||||||
|
private var scale18Adapter: Scale18GridAdapter? = null
|
||||||
|
|
||||||
/** 已连接的主设备数量(有连接即代表主设备在线) */
|
/** 已连接的主设备数量(有连接即代表主设备在线) */
|
||||||
private var masterConnected = false
|
private var masterConnected = false
|
||||||
@@ -41,23 +53,73 @@ class SlaveActivity : BaseActivity() {
|
|||||||
|
|
||||||
binding.tvDeviceId.text = "设备:${GlobalData.deviceId}"
|
binding.tvDeviceId.text = "设备:${GlobalData.deviceId}"
|
||||||
binding.tvDeviceIp.text = "IP:${getLocalIpAddress()}"
|
binding.tvDeviceIp.text = "IP:${getLocalIpAddress()}"
|
||||||
binding.rvScaleList.layoutManager = LinearLayoutManager(this)
|
|
||||||
binding.rvScaleList.itemAnimator = null // 关闭默认动画,避免数据频繁更新时出现闪烁错乱
|
|
||||||
binding.rvScaleList.adapter = adapter
|
|
||||||
|
|
||||||
|
setupScaleList()
|
||||||
updateMasterStatus(ScaleServiceManager.isMasterConnected)
|
updateMasterStatus(ScaleServiceManager.isMasterConnected)
|
||||||
listenLocalScales()
|
listenLocalScales()
|
||||||
listenMasterConnection()
|
listenMasterConnection()
|
||||||
addBackKeyListener()
|
addBackKeyListener()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据 deviceId 选择对应的布局方式:
|
||||||
|
* - 22个秤:FlexboxLayoutManager + Scale22GridAdapter
|
||||||
|
* - 18个秤:GridLayoutManager(6列) + Scale18GridAdapter
|
||||||
|
* - 其他:LinearLayoutManager + 线性列表
|
||||||
|
*/
|
||||||
|
private fun setupScaleList() {
|
||||||
|
binding.rvScaleList.itemAnimator = null
|
||||||
|
when (GlobalData.deviceId) {
|
||||||
|
ScaleDeviceConfig.DEVICE_ID_22 -> {
|
||||||
|
val screenWidth = resources.displayMetrics.widthPixels
|
||||||
|
val usable = screenWidth - 8 * 4.dp - 10.dp
|
||||||
|
val smallSize = usable / 9
|
||||||
|
val largeSize = (usable - smallSize * 6) / 2
|
||||||
|
val rvHeight = smallSize * 3 + 3 * 4.dp
|
||||||
|
binding.rvScaleList.updateLayoutParams<LinearLayout.LayoutParams> {
|
||||||
|
height = rvHeight
|
||||||
|
weight = 0f
|
||||||
|
topMargin = 20.dp
|
||||||
|
}
|
||||||
|
binding.rvScaleList.layoutManager = FlexboxLayoutManager(this).apply {
|
||||||
|
flexDirection = FlexDirection.COLUMN
|
||||||
|
flexWrap = FlexWrap.WRAP
|
||||||
|
alignItems = AlignItems.FLEX_START
|
||||||
|
}
|
||||||
|
scale22Adapter = Scale22GridAdapter(largeSize, smallSize).also {
|
||||||
|
it.onItemClick = { scale -> showTareDialog(scale.address) }
|
||||||
|
}
|
||||||
|
binding.rvScaleList.adapter = scale22Adapter
|
||||||
|
}
|
||||||
|
ScaleDeviceConfig.DEVICE_ID_18 -> {
|
||||||
|
binding.rvScaleList.updateLayoutParams<LinearLayout.LayoutParams> { topMargin = 0 }
|
||||||
|
binding.rvScaleList.layoutManager = GridLayoutManager(this, 6).apply {
|
||||||
|
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||||
|
override fun getSpanSize(position: Int) = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scale18Adapter = Scale18GridAdapter().also {
|
||||||
|
it.onItemClick = { scale -> showTareDialog(scale.address) }
|
||||||
|
}
|
||||||
|
binding.rvScaleList.adapter = scale18Adapter
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
binding.rvScaleList.updateLayoutParams<LinearLayout.LayoutParams> { topMargin = 20.dp }
|
||||||
|
binding.rvScaleList.layoutManager = LinearLayoutManager(this)
|
||||||
|
linearAdapter.setOnItemClickListener { _, _, position ->
|
||||||
|
val item = linearAdapter.items.getOrNull(position) ?: return@setOnItemClickListener
|
||||||
|
showTareDialog(item.address)
|
||||||
|
}
|
||||||
|
binding.rvScaleList.adapter = linearAdapter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 禁用返回键,防止退回 InitActivity 后重复打开本页
|
* 禁用返回键,防止退回 InitActivity 后重复打开本页
|
||||||
*/
|
*/
|
||||||
private fun addBackKeyListener() {
|
private fun addBackKeyListener() {
|
||||||
onBackPressedDispatcher.addCallback(this) {
|
onBackPressedDispatcher.addCallback(this) { }
|
||||||
// 禁用返回键,不执行任何操作
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,19 +133,39 @@ class SlaveActivity : BaseActivity() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听本机 WeightUtil 回调,实时刷新秤列表
|
* 监听本机 WeightUtil 回调,实时刷新秤列表
|
||||||
|
* 22/18个秤走网格 adapter,其余走线性列表
|
||||||
*/
|
*/
|
||||||
private fun listenLocalScales() {
|
private fun listenLocalScales() {
|
||||||
WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
val idx = scaleList.indexOfFirst { it.address == address }
|
when {
|
||||||
val item = ScaleItem(address, weight, state)
|
scale22Adapter != null -> {
|
||||||
if (idx >= 0) {
|
// 用最新数据更新缓存后整体刷新网格
|
||||||
scaleList[idx] = item
|
val existing = scale22Adapter!!.items.toMutableList()
|
||||||
adapter.notifyItemChanged(idx)
|
val idx = existing.indexOfFirst { it.address == address }
|
||||||
} else {
|
val newData = ScaleData(GlobalData.deviceId, address, weight, state, System.currentTimeMillis())
|
||||||
scaleList.add(item)
|
if (idx >= 0) existing[idx] = newData else existing.add(newData)
|
||||||
scaleList.sortBy { it.address }
|
scale22Adapter!!.updateByAddress(existing)
|
||||||
adapter.notifyDataSetChanged()
|
}
|
||||||
|
scale18Adapter != null -> {
|
||||||
|
val existing = scale18Adapter!!.items.toMutableList()
|
||||||
|
val idx = existing.indexOfFirst { it.address == address }
|
||||||
|
val newData = ScaleData(GlobalData.deviceId, address, weight, state, System.currentTimeMillis())
|
||||||
|
if (idx >= 0) existing[idx] = newData else existing.add(newData)
|
||||||
|
scale18Adapter!!.updateByAddress(existing)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val item = ScaleRowAdapter.ScaleItem(address, weight, state)
|
||||||
|
val list = linearAdapter.items.toMutableList()
|
||||||
|
val idx = list.indexOfFirst { it.address == address }
|
||||||
|
if (idx >= 0) {
|
||||||
|
list[idx] = item
|
||||||
|
} else {
|
||||||
|
list.add(item)
|
||||||
|
list.sortBy { it.address }
|
||||||
|
}
|
||||||
|
linearAdapter.submitList(list)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +173,6 @@ class SlaveActivity : BaseActivity() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新主设备连接状态文字
|
* 更新主设备连接状态文字
|
||||||
* 由 ScaleWebSocketServer 的 onOpen/onClose 回调驱动(可在后续扩展中接入)
|
|
||||||
*/
|
*/
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
fun updateMasterStatus(connected: Boolean) {
|
fun updateMasterStatus(connected: Boolean) {
|
||||||
@@ -112,37 +193,27 @@ class SlaveActivity : BaseActivity() {
|
|||||||
const val TAG = "SlaveActivity"
|
const val TAG = "SlaveActivity"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹出清零确认对话框
|
||||||
|
* @param address 目标秤地址
|
||||||
|
*/
|
||||||
|
private fun showTareDialog(address: Int) {
|
||||||
|
CommonDialog(this)
|
||||||
|
.setTitle("清零确认")
|
||||||
|
.setContent("确认对秤 $address 执行清零操作?")
|
||||||
|
.setNegativeButton("取消")
|
||||||
|
.setPositiveButton("确认") {
|
||||||
|
WeightUtil.tareTwo(address)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取本机局域网 IP,委托给 NetworkUtil 统一处理版本兼容
|
* 获取本机局域网 IP,委托给 NetworkUtil 统一处理版本兼容
|
||||||
* 获取失败时返回"未知"
|
|
||||||
*/
|
*/
|
||||||
private fun getLocalIpAddress(): String {
|
private fun getLocalIpAddress(): String {
|
||||||
return NetworkUtil.getLocalIpAddress(this).ifEmpty { "未知" }
|
return NetworkUtil.getLocalIpAddress(this).ifEmpty { "未知" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ScaleAdapter(private val data: List<ScaleItem>) :
|
|
||||||
RecyclerView.Adapter<ScaleAdapter.VH>() {
|
|
||||||
|
|
||||||
inner class VH(val binding: ListItemScaleRowBinding) :
|
|
||||||
RecyclerView.ViewHolder(binding.root)
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = VH(
|
|
||||||
ListItemScaleRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getItemCount() = data.size
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: VH, position: Int) {
|
|
||||||
val item = data[position]
|
|
||||||
val stateStr = when (item.state) {
|
|
||||||
WeightUtil.STATE_STABLE -> "稳定"
|
|
||||||
WeightUtil.STATE_UNSTABLE -> "不稳定"
|
|
||||||
WeightUtil.STATE_OVER_WEIGHT -> "超量"
|
|
||||||
else -> "${item.state}"
|
|
||||||
}
|
|
||||||
holder.binding.tvScaleLabel.text = "秤 ${item.address}"
|
|
||||||
holder.binding.tvWeight.text = "${item.weight} g"
|
|
||||||
holder.binding.tvState.text = stateStr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import com.shuwei.dish.match.base.BaseApp
|
|||||||
import com.shuwei.dish.match.base.BaseFragment
|
import com.shuwei.dish.match.base.BaseFragment
|
||||||
import com.shuwei.dish.match.databinding.FragmentDeviceConfigBinding
|
import com.shuwei.dish.match.databinding.FragmentDeviceConfigBinding
|
||||||
import com.shuwei.dish.match.db.AppRepository
|
import com.shuwei.dish.match.db.AppRepository
|
||||||
import com.shuwei.dish.match.dialog.CommonDialog
|
|
||||||
import com.shuwei.dish.match.dialog.SeasoningSearchDialog
|
import com.shuwei.dish.match.dialog.SeasoningSearchDialog
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||||
import com.shuwei.dish.match.entity.ResetReasoningRecord
|
import com.shuwei.dish.match.entity.ResetReasoningRecord
|
||||||
@@ -39,7 +38,6 @@ import com.shuwei.dish.match.utils.ext.toast
|
|||||||
import com.shuwei.dish.match.utils.ext.visible
|
import com.shuwei.dish.match.utils.ext.visible
|
||||||
import com.shuwei.dish.match.viewmodel.AppViewModel
|
import com.shuwei.dish.match.viewmodel.AppViewModel
|
||||||
import com.shuwei.dish.match.viewmodel.factory.AppFactory
|
import com.shuwei.dish.match.viewmodel.factory.AppFactory
|
||||||
import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding
|
|
||||||
import com.shuwei.dish.match.ui.SamplingListActivity
|
import com.shuwei.dish.match.ui.SamplingListActivity
|
||||||
import com.shuwei.dish.match.ui.SelectDishActivity
|
import com.shuwei.dish.match.ui.SelectDishActivity
|
||||||
import com.shuwei.dish.match.utils.SpTool
|
import com.shuwei.dish.match.utils.SpTool
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -21,8 +21,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="30dp"
|
android:layout_marginStart="30dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingTop="30dp"
|
|
||||||
android:paddingBottom="30dp"
|
|
||||||
android:textColor="@color/black"
|
android:textColor="@color/black"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp"
|
||||||
|
tools:background="@color/white">
|
||||||
|
|
||||||
|
<!-- 食材照片,权重占满剩余空间 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivFoodPhoto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@color/bg_color" />
|
||||||
|
|
||||||
|
<!-- 识别到的食材列表,无数据时隐藏 -->
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:maxHeight="200dp"
|
||||||
|
android:nestedScrollingEnabled="false"
|
||||||
|
android:overScrollMode="never"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:spanCount="2"
|
||||||
|
tools:itemCount="4"
|
||||||
|
tools:listitem="@layout/list_item_search_goods_info"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<!-- 选中的食材名称 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvSelectedFood"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="-"
|
||||||
|
android:textColor="@color/black333"
|
||||||
|
android:textSize="32sp" />
|
||||||
|
|
||||||
|
<!-- 秤1重量 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvWeight"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/shape_white_12_corners"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="0.0 g"
|
||||||
|
android:textColor="@color/black333"
|
||||||
|
android:textSize="32sp" />
|
||||||
|
|
||||||
|
<!-- 更换食材 -->
|
||||||
|
<androidx.appcompat.widget.AppCompatButton
|
||||||
|
android:id="@+id/btnChangeFood"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:background="@drawable/shape_green_bg"
|
||||||
|
android:text="更换食材"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- 取消 -->
|
||||||
|
<androidx.appcompat.widget.AppCompatButton
|
||||||
|
android:id="@+id/btnCancel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="90dp"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:layout_marginBottom="30dp"
|
||||||
|
android:background="@drawable/shape_white_12_corners"
|
||||||
|
android:text="取消"
|
||||||
|
android:textColor="@color/black333"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -4,20 +4,36 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="12dp"
|
android:paddingHorizontal="5dp"
|
||||||
tools:background="@color/bg_color">
|
tools:background="@color/bg_color">
|
||||||
|
|
||||||
<!-- 本机IP地址 -->
|
<!-- IP 与主设备状态同行显示 -->
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/tvDeviceIp"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_marginTop="0dp"
|
android:gravity="center">
|
||||||
android:textSize="22sp"
|
|
||||||
android:textStyle="bold"
|
<!-- 本机IP地址 -->
|
||||||
android:textColor="@color/home_title"
|
<TextView
|
||||||
tools:text="IP:192.168.1.100" />
|
android:id="@+id/tvDeviceIp"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/home_title"
|
||||||
|
tools:text="IP:192.168.1.100" />
|
||||||
|
|
||||||
|
<!-- 主设备连接状态 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMasterStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="主设备:未连接" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 设备信息 -->
|
<!-- 设备信息 -->
|
||||||
<TextView
|
<TextView
|
||||||
@@ -25,22 +41,10 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:layout_marginTop="0dp"
|
android:textSize="16sp"
|
||||||
android:textSize="18sp"
|
|
||||||
android:textColor="@color/home_sub_title"
|
android:textColor="@color/home_sub_title"
|
||||||
tools:text="设备:device_02" />
|
tools:text="设备:device_02" />
|
||||||
|
|
||||||
<!-- 主设备连接状态 -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvMasterStatus"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="0dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:textSize="16sp"
|
|
||||||
tools:text="主设备:未连接" />
|
|
||||||
|
|
||||||
<!-- 本机秤数据列表 -->
|
<!-- 本机秤数据列表 -->
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/rvScaleList"
|
android:id="@+id/rvScaleList"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="30dp"
|
android:layout_marginStart="30dp"
|
||||||
@@ -8,10 +7,29 @@
|
|||||||
android:background="@drawable/shape_white_30_corners"
|
android:background="@drawable/shape_white_30_corners"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<FrameLayout
|
<TextView
|
||||||
android:id="@+id/llContent"
|
android:id="@+id/tvTitle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="40dp"
|
||||||
|
android:layout_marginTop="55dp"
|
||||||
|
android:layout_marginEnd="40dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/black"
|
||||||
|
android:textSize="36sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvContent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="40dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginEnd="40dp"
|
||||||
|
android:layout_marginBottom="55dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/black999"
|
||||||
|
android:textSize="26sp" />
|
||||||
|
|
||||||
<com.google.android.material.divider.MaterialDivider
|
<com.google.android.material.divider.MaterialDivider
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -20,7 +38,7 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="120dp"
|
android:layout_height="100dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
@@ -32,8 +50,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:background="@drawable/ripple_effect_light2"
|
android:background="@drawable/ripple_effect_light2"
|
||||||
android:textColor="@color/dish_green"
|
android:textColor="@color/dish_green"
|
||||||
android:textSize="30sp"
|
android:textSize="30sp" />
|
||||||
tools:text="取消" />
|
|
||||||
|
|
||||||
<com.google.android.material.divider.MaterialDivider
|
<com.google.android.material.divider.MaterialDivider
|
||||||
android:layout_width="1dp"
|
android:layout_width="1dp"
|
||||||
@@ -49,8 +66,7 @@
|
|||||||
android:textColor="@color/dish_green"
|
android:textColor="@color/dish_green"
|
||||||
android:background="@drawable/ripple_effect_light2"
|
android:background="@drawable/ripple_effect_light2"
|
||||||
android:textSize="30sp"
|
android:textSize="30sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold" />
|
||||||
tools:text="确定" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -46,11 +46,22 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/ivOperateIcon"
|
android:id="@+id/ivOperateIcon"
|
||||||
android:layout_width="60dp"
|
android:layout_width="80dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_marginEnd="30dp"
|
|
||||||
tools:src="@drawable/ic_dish_selected"
|
tools:src="@drawable/ic_dish_selected"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/ivClearIcon"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivClearIcon"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
tools:src="@drawable/ic_dish_clear"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
|
|||||||
Reference in New Issue
Block a user