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:screenOrientation="portrait"
|
||||
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
|
||||
android:name="com.shuwei.dish.match.ui.SlaveActivity"
|
||||
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.ui.InitActivity
|
||||
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.invisible
|
||||
import com.shuwei.dish.match.utils.ext.visible
|
||||
@@ -65,6 +66,10 @@ open class BaseActivity : AppCompatActivity() {
|
||||
// systemBarsBehavior =
|
||||
// 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.ivBack.setOnClickListener { finish() }
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import android.widget.LinearLayout
|
||||
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.utils.ext.dp
|
||||
import java.lang.ref.WeakReference
|
||||
@@ -20,53 +22,74 @@ open class CommonDialog(
|
||||
|
||||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
binding = DialogCommonBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
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 {
|
||||
width = 708.dp
|
||||
width = if (isMaster) (screenWidth * 0.72f).toInt() else (screenWidth * 0.60f).toInt()
|
||||
}
|
||||
setBackgroundDrawable(ColorDrawable())
|
||||
setCancelable(false)
|
||||
// setFlags(
|
||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
// )
|
||||
}
|
||||
|
||||
binding.llContent.run {
|
||||
removeAllViews()
|
||||
addView(messageView)
|
||||
val verticalMargin = if (GlobalData.deviceRole == DeviceRole.MASTER) 35.dp else 20.dp
|
||||
binding.tvTitle.updateLayoutParams<LinearLayout.LayoutParams> { topMargin = verticalMargin }
|
||||
binding.tvContent.updateLayoutParams<LinearLayout.LayoutParams> { bottomMargin = verticalMargin }
|
||||
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() {
|
||||
super.show()
|
||||
binding.btnLeft.text = leftText
|
||||
binding.btnRight.text = rightText
|
||||
binding.tvTitle.text = titleText
|
||||
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) :
|
||||
|
||||
@@ -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)
|
||||
window?.run {
|
||||
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
|
||||
setFlags(
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
);
|
||||
// setFlags(
|
||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
// WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
// );
|
||||
}
|
||||
setOnDismissListener {
|
||||
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 客户端
|
||||
val client = ScaleWebSocketClient().also {
|
||||
it.onScaleData = { data -> aggregator?.onRemoteScaleData(data) }
|
||||
it.onDeviceDisconnected = { remoteId -> aggregator?.removeDevice(remoteId) }
|
||||
}
|
||||
wsClient = client
|
||||
|
||||
@@ -141,12 +142,13 @@ object ScaleServiceManager {
|
||||
* @param deviceId 目标设备 ID
|
||||
* @param address 目标秤地址
|
||||
*/
|
||||
fun sendTare(deviceId: String, address: Int) {
|
||||
if (deviceId == GlobalData.deviceId) {
|
||||
fun sendTare(deviceId: String, address: Int): Boolean {
|
||||
return if (deviceId == GlobalData.deviceId) {
|
||||
com.shuwei.dish.match.utils.WeightUtil.tareTwo(address)
|
||||
true
|
||||
} else {
|
||||
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 onDeviceDisconnected: ((deviceId: String) -> Unit)? = null
|
||||
|
||||
/**
|
||||
* 连接到指定子设备
|
||||
* @param deviceId 子设备 ID
|
||||
@@ -85,12 +88,14 @@ class ScaleWebSocketClient {
|
||||
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||
Log.w(TAG, "连接子设备失败: $deviceId, ${t.message}")
|
||||
connections.remove(deviceId)
|
||||
onDeviceDisconnected?.invoke(deviceId)
|
||||
scheduleReconnect(deviceId, host, port)
|
||||
}
|
||||
|
||||
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
||||
Log.d(TAG, "子设备连接已关闭: $deviceId, reason=$reason")
|
||||
connections.remove(deviceId)
|
||||
onDeviceDisconnected?.invoke(deviceId)
|
||||
scheduleReconnect(deviceId, host, port)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -15,7 +15,6 @@ import androidx.core.view.forEach
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
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.dialog.CommonDialog
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
@@ -169,21 +168,13 @@ class DeviceConfigActivity : BaseActivity() {
|
||||
val detector = MultiClickDetector(targetCount = 10, intervalMs = 800)
|
||||
|
||||
private fun defaultDataSettingDialog() {
|
||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
||||
remindBinding.tvDialogTitle.text = "温馨提示"
|
||||
remindBinding.tvDialogContent.text =
|
||||
"您好,使用默认配置作为您的调料数据,则已有配置将被清除,确认吗?"
|
||||
CommonDialog(this).apply {
|
||||
messageView = remindBinding.root
|
||||
leftText = "取消"
|
||||
rightText = "确认"
|
||||
onLeftClick = { dismiss() }
|
||||
onRightClick = {
|
||||
dismiss()
|
||||
resetList()
|
||||
}
|
||||
onDismiss = { hideStatusBar() }
|
||||
}.show()
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setContent("您好,使用默认配置作为您的调料数据,则已有配置将被清除,确认吗?")
|
||||
.setNegativeButton("取消")
|
||||
.setPositiveButton("确认") { resetList() }
|
||||
.setOnDismissCallback { hideStatusBar() }
|
||||
.show()
|
||||
}
|
||||
|
||||
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.BaseApp
|
||||
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.dialog.FoodSearchDialog
|
||||
import com.shuwei.dish.match.dialog.CommonDialog
|
||||
@@ -130,23 +129,18 @@ class DishSamplingActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun saveDataRemindDialog() {
|
||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
||||
remindBinding.tvDialogTitle.text = "温馨提示"
|
||||
remindBinding.tvDialogContent.text = "您好,当前页面存在未保存的数据,确认返回吗?"
|
||||
CommonDialog(this).apply {
|
||||
messageView = remindBinding.root
|
||||
leftText = "取消"
|
||||
rightText = "确认"
|
||||
onLeftClick = { dismiss() }
|
||||
onRightClick = {
|
||||
dismiss()
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setContent("您好,当前页面存在未保存的数据,确认返回吗?")
|
||||
.setNegativeButton("取消")
|
||||
.setPositiveButton("确认") {
|
||||
if (pageFrom == HOME) {
|
||||
startActivity<SamplingListActivity>()
|
||||
}
|
||||
finish()
|
||||
}
|
||||
onDismiss = { hideStatusBar() }
|
||||
}.show()
|
||||
.setOnDismissCallback { hideStatusBar() }
|
||||
.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.RecyclerView
|
||||
import com.chad.library.adapter4.BaseMultiItemAdapter
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
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.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.dialog.CommonDialog
|
||||
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.ListTypeScale18Binding
|
||||
import com.shuwei.dish.match.databinding.ListTypeScale2Binding
|
||||
import com.shuwei.dish.match.databinding.ListTypeScale22Binding
|
||||
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.utils.NetworkUtil
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import com.shuwei.dish.match.utils.ext.toast
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
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_SCALE22 = 1
|
||||
private const val VIEW_TYPE_SCALE18 = 2
|
||||
@@ -89,7 +83,7 @@ class MasterScaleActivity : BaseActivity() {
|
||||
.groupBy { it.deviceId }
|
||||
.entries
|
||||
.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) ->
|
||||
DeviceGroup(
|
||||
@@ -99,17 +93,43 @@ class MasterScaleActivity : BaseActivity() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun updateList(groups: List<DeviceGroup>) {
|
||||
groupList.clear()
|
||||
groupList.addAll(groups)
|
||||
adapter.notifyDataSetChanged()
|
||||
if (groupList.isEmpty()) {
|
||||
groupList.addAll(groups)
|
||||
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")
|
||||
@@ -122,6 +142,18 @@ class MasterScaleActivity : BaseActivity() {
|
||||
private inner class DeviceGroupAdapter(data: MutableList<DeviceGroup>) :
|
||||
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 {
|
||||
addItemType(VIEW_TYPE_SCALE2, object : BaseMultiItemAdapter.OnMultiItemAdapterListener<DeviceGroup, QuickViewHolder> {
|
||||
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?) {
|
||||
item ?: return
|
||||
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> {
|
||||
@@ -138,7 +172,10 @@ class MasterScaleActivity : BaseActivity() {
|
||||
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
||||
item ?: return
|
||||
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> {
|
||||
@@ -147,7 +184,9 @@ class MasterScaleActivity : BaseActivity() {
|
||||
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
||||
item ?: return
|
||||
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> {
|
||||
@@ -156,13 +195,15 @@ class MasterScaleActivity : BaseActivity() {
|
||||
override fun onBind(holder: QuickViewHolder, position: Int, item: DeviceGroup?) {
|
||||
item ?: return
|
||||
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) {
|
||||
DEVICE_ID_2 -> VIEW_TYPE_SCALE2
|
||||
DEVICE_ID_22 -> VIEW_TYPE_SCALE22
|
||||
DEVICE_ID_18 -> VIEW_TYPE_SCALE18
|
||||
ScaleDeviceConfig.DEVICE_ID_2 -> VIEW_TYPE_SCALE2
|
||||
ScaleDeviceConfig.DEVICE_ID_22 -> VIEW_TYPE_SCALE22
|
||||
ScaleDeviceConfig.DEVICE_ID_18 -> VIEW_TYPE_SCALE18
|
||||
else -> VIEW_TYPE_SCALE1
|
||||
}}
|
||||
}
|
||||
@@ -171,14 +212,36 @@ class MasterScaleActivity : BaseActivity() {
|
||||
// ── ViewHolder: 2个秤 ──────────────────────────────────────────────────
|
||||
|
||||
private inner class Scale2VH(val b: ListTypeScale2Binding) : RecyclerView.ViewHolder(b.root) {
|
||||
private lateinit var cells: List<LinearLayout>
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(group: DeviceGroup) {
|
||||
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
||||
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
||||
b.llScaleContainer.removeAllViews()
|
||||
val cellSize = (resources.displayMetrics.widthPixels - 4 * 4.dp - 10.dp) / 2
|
||||
group.scales.take(2).forEach { scale ->
|
||||
b.llScaleContainer.addView(makeScaleCell(scale, cellSize))
|
||||
|
||||
if (!::cells.isInitialized) {
|
||||
val cellSize = (resources.displayMetrics.widthPixels - 4 * 4.dp - 10.dp) / 2
|
||||
cells = group.scales.take(2).map { scale ->
|
||||
makeScaleCell(scale, cellSize).also { cell ->
|
||||
cell.setOnClickListener {
|
||||
val s = cell.tag as? ScaleData ?: return@setOnClickListener
|
||||
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个秤 ─────────────────────────────────────────────────
|
||||
|
||||
private inner class Scale22VH(val b: ListTypeScale22Binding) : RecyclerView.ViewHolder(b.root) {
|
||||
private var bound = false
|
||||
private var innerAdapter: Scale22GridAdapter? = null
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(group: DeviceGroup) {
|
||||
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
||||
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
||||
|
||||
if (!bound) {
|
||||
// 计算格子尺寸(与 Scale22GridActivity 一致)
|
||||
if (innerAdapter == null) {
|
||||
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 largeSize = (usable - smallSize * 6) / 2
|
||||
val rvHeight = smallSize * 3 + 3 * 4.dp
|
||||
@@ -207,154 +269,83 @@ class MasterScaleActivity : BaseActivity() {
|
||||
alignItems = AlignItems.FLEX_START
|
||||
}
|
||||
b.rvScales.itemAnimator = null
|
||||
b.rvScales.adapter = Scale22InnerAdapter(largeSize, smallSize)
|
||||
bound = true
|
||||
innerAdapter = Scale22GridAdapter(largeSize, smallSize).apply {
|
||||
onItemClick = { scale ->
|
||||
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
||||
}
|
||||
}
|
||||
b.rvScales.adapter = innerAdapter
|
||||
}
|
||||
val scaleByAddress = group.scales.associateBy { it.address }
|
||||
val ordered = SCALE_ORDER_22.map { scaleByAddress[it] }
|
||||
(b.rvScales.adapter as Scale22InnerAdapter).update(ordered)
|
||||
updateScales(group.scales)
|
||||
}
|
||||
|
||||
/** 仅更新秤数据,不触发外层任何 notify */
|
||||
fun updateScales(scales: List<ScaleData>) {
|
||||
innerAdapter?.updateByAddress(scales)
|
||||
}
|
||||
}
|
||||
|
||||
// ── ViewHolder: 18个秤 ─────────────────────────────────────────────────
|
||||
|
||||
private inner class Scale18VH(val b: ListTypeScale18Binding) : RecyclerView.ViewHolder(b.root) {
|
||||
private var bound = false
|
||||
private var innerAdapter: Scale18GridAdapter? = null
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bind(group: DeviceGroup) {
|
||||
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
||||
b.tvDeviceLabel.text = "设备:${group.deviceId}"
|
||||
|
||||
if (!bound) {
|
||||
if (innerAdapter == null) {
|
||||
val gridLayoutManager = GridLayoutManager(b.root.context, 6).apply {
|
||||
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
// 每个格子占1 span,共6列
|
||||
override fun getSpanSize(position: Int) = 1
|
||||
}
|
||||
}
|
||||
b.rvScales.layoutManager = gridLayoutManager
|
||||
b.rvScales.itemAnimator = null
|
||||
b.rvScales.adapter = Scale18InnerAdapter()
|
||||
bound = true
|
||||
innerAdapter = Scale18GridAdapter().apply {
|
||||
onItemClick = { scale ->
|
||||
showTareDialog(deviceId = scale.deviceId, ip = scale.ip, address = scale.address)
|
||||
}
|
||||
}
|
||||
b.rvScales.adapter = innerAdapter
|
||||
}
|
||||
val scaleByAddress = group.scales.associateBy { it.address }
|
||||
val ordered = SCALE_ORDER_18.map { scaleByAddress[it] }
|
||||
(b.rvScales.adapter as Scale18InnerAdapter).update(ordered)
|
||||
updateScales(group.scales)
|
||||
}
|
||||
|
||||
/** 仅更新秤数据,不触发外层任何 notify */
|
||||
fun updateScales(scales: List<ScaleData>) {
|
||||
innerAdapter?.updateByAddress(scales)
|
||||
}
|
||||
}
|
||||
|
||||
// ── ViewHolder: 1个秤 ──────────────────────────────────────────────────
|
||||
|
||||
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")
|
||||
fun bind(group: DeviceGroup) {
|
||||
b.tvDeviceIp.text = "IP:${group.ip.ifEmpty { "未知" }}"
|
||||
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.tvWeight.text = scale?.let { "${it.weight} g" } ?: ""
|
||||
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) {
|
||||
@@ -365,7 +356,7 @@ class MasterScaleActivity : BaseActivity() {
|
||||
else -> "$state"
|
||||
}
|
||||
|
||||
/** 创建正方形秤格子(用于 Scale2VH) */
|
||||
/** 创建正方形秤格子(用于 Scale2VH 初始化) */
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun makeScaleCell(scale: ScaleData, cellSize: Int): LinearLayout {
|
||||
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.weight}g", (baseSize * 0.22f).coerceIn(12f, 20f).toInt(), bold = true))
|
||||
addView(makeCellText(stateStr(scale.state), (baseSize * 0.14f).coerceIn(8f, 12f).toInt()))
|
||||
tag = scale
|
||||
}
|
||||
}
|
||||
|
||||
/** 仅更新格子内文字,不重建 view(用于 Scale2VH 数据刷新) */
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun updateScaleCell(cell: LinearLayout, scale: ScaleData) {
|
||||
(cell.getChildAt(0) as? TextView)?.text = "秤${scale.address}"
|
||||
(cell.getChildAt(1) as? TextView)?.text = "${scale.weight}g"
|
||||
(cell.getChildAt(2) as? TextView)?.text = stateStr(scale.state)
|
||||
}
|
||||
|
||||
private fun makeCellText(text: String, spSize: Int, bold: Boolean = false): TextView =
|
||||
TextView(this).apply {
|
||||
this.text = text
|
||||
@@ -395,4 +395,23 @@ class MasterScaleActivity : BaseActivity() {
|
||||
gravity = Gravity.CENTER
|
||||
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.base.BaseActivity
|
||||
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.FoodRecognizeDialog
|
||||
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
||||
import com.shuwei.dish.match.entity.CookFoodEntity
|
||||
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
||||
@@ -159,6 +157,10 @@ class PrepareCookActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
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 currentWeight = 0.0
|
||||
|
||||
@@ -169,10 +171,11 @@ class PrepareCookActivity : BaseActivity() {
|
||||
private fun recognizeFood(weight: Double) {
|
||||
this.currentWeight = weight
|
||||
if (weight < WEIGHT_RECOGNIZE_VALUE) {
|
||||
if (recognizeDialog.isShowing) recognizeDialog.dismiss()
|
||||
isRecognizeOpen = false
|
||||
FoodRecognizeActivity.close()
|
||||
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)
|
||||
cameraUtils.takePhoto(
|
||||
succCallback = cameraSuccessCallback,
|
||||
@@ -195,9 +198,13 @@ class PrepareCookActivity : BaseActivity() {
|
||||
// materialType =
|
||||
isNewDishType = true
|
||||
isOriginalData = false
|
||||
useWeight = lastWeight
|
||||
isSetFinished = true
|
||||
})
|
||||
onFoodItemClick(list.size - 1)
|
||||
binding.rvDishPartList.smoothScrollToPosition(list.size - 1)
|
||||
val positon = list.size - 1
|
||||
dishPartAdapter.notifyItemInserted(positon)
|
||||
onFoodItemClick(positon)
|
||||
binding.rvDishPartList.smoothScrollToPosition(positon)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,18 +246,12 @@ class PrepareCookActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun showRemindDialog() {
|
||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
||||
CommonDialog(this).apply {
|
||||
messageView = remindBinding.root
|
||||
leftText = "返回调整"
|
||||
rightText = "确认无误"
|
||||
onLeftClick = { dismiss() }
|
||||
onRightClick = {
|
||||
openSubmitPage()
|
||||
dismiss()
|
||||
}
|
||||
onDismiss = { hideStatusBar() }
|
||||
}.show()
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setNegativeButton("返回调整")
|
||||
.setPositiveButton("确认无误") { openSubmitPage() }
|
||||
.setOnDismissCallback { hideStatusBar() }
|
||||
.show()
|
||||
}
|
||||
|
||||
|
||||
@@ -305,18 +306,24 @@ class PrepareCookActivity : BaseActivity() {
|
||||
|
||||
private var firstReqSize = 0
|
||||
private val list = mutableListOf<CookFoodGoodsEntity>()
|
||||
private val recognizeDialog by lazy {
|
||||
FoodRecognizeDialog(
|
||||
activity = this,
|
||||
onDismiss = { isTakingPhoto.set(false) },
|
||||
onItemSelected = foodSelectCallback
|
||||
)
|
||||
}
|
||||
private val dishPartAdapter by lazy {
|
||||
DishPartAdapter(list).apply {
|
||||
setOnItemClickListener { _, _, 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() {
|
||||
super.onResume()
|
||||
isRecognizeEnabled = true
|
||||
isRecognizeOpen = false
|
||||
cameraUtils.bind()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
isRecognizeEnabled = false
|
||||
cameraUtils.unbind()
|
||||
}
|
||||
|
||||
@@ -431,20 +441,13 @@ class PrepareCookActivity : BaseActivity() {
|
||||
* 未保存数据提醒
|
||||
*/
|
||||
private fun saveDataRemindDialog() {
|
||||
val remindBinding = LayoutFoodRemindBinding.inflate(layoutInflater)
|
||||
remindBinding.tvDialogTitle.text = "温馨提示"
|
||||
remindBinding.tvDialogContent.text = "您好,当前页面存在尚未保存的数据,确认返回上页吗?"
|
||||
CommonDialog(this).apply {
|
||||
messageView = remindBinding.root
|
||||
leftText = "取消"
|
||||
rightText = "确认"
|
||||
onLeftClick = { dismiss() }
|
||||
onRightClick = {
|
||||
dismiss()
|
||||
finish()
|
||||
}
|
||||
onDismiss = { hideStatusBar() }
|
||||
}.show()
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setContent("您好,当前页面存在尚未保存的数据,确认返回吗?")
|
||||
.setNegativeButton("取消")
|
||||
.setPositiveButton("确认") { finish() }
|
||||
.setOnDismissCallback { hideStatusBar() }
|
||||
.show()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,6 +455,7 @@ class PrepareCookActivity : BaseActivity() {
|
||||
*/
|
||||
private val cameraSuccessCallback: (Uri) -> Unit = { uri ->
|
||||
Log.d(TAG, "takePhoto success")
|
||||
lastPhotoUri = uri
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val bitmap = ImageUtil.uriToBitmap(this@PrepareCookActivity, uri)
|
||||
@@ -499,7 +503,13 @@ class PrepareCookActivity : BaseActivity() {
|
||||
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)
|
||||
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.LayoutEmptyViewBinding
|
||||
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.FoodRecord
|
||||
import com.shuwei.dish.match.utils.DateTimeUtil
|
||||
@@ -378,16 +378,12 @@ class SamplingListActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun showDeviceConfigDialog() {
|
||||
DialogTool.load(
|
||||
context = this,
|
||||
title = "温馨提示",
|
||||
content = "请先在设备配置页面进行调料设置",
|
||||
leftBtnText = "取消",
|
||||
rightBtnText = "去设置",
|
||||
leftBtnClick = {},
|
||||
rightBtnClick = {
|
||||
startActivity<SettingActivity>()
|
||||
})
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setContent("请先在设备配置页面进行调料设置")
|
||||
.setNegativeButton("取消")
|
||||
.setPositiveButton("去设置") { startActivity<SettingActivity>() }
|
||||
.show()
|
||||
}
|
||||
|
||||
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.databinding.ActivitySelectDishBinding
|
||||
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.FoodRecord
|
||||
import com.shuwei.dish.match.ui.fragment.DishListFragment
|
||||
@@ -95,17 +95,12 @@ class SelectDishActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun showDeviceConfigDialog() {
|
||||
DialogTool.load(
|
||||
context = this,
|
||||
title = "温馨提示",
|
||||
content = "请先在设备配置页面进行调料设置",
|
||||
leftBtnText = "取消",
|
||||
rightBtnText = "去设置",
|
||||
leftBtnClick = {},
|
||||
rightBtnClick = {
|
||||
startActivity<SettingActivity>()
|
||||
}
|
||||
)
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setContent("请先在设备配置页面进行调料设置")
|
||||
.setNegativeButton("取消")
|
||||
.setPositiveButton("去设置") { startActivity<SettingActivity>() }
|
||||
.show()
|
||||
}
|
||||
|
||||
fun addViewListener() {
|
||||
|
||||
@@ -2,33 +2,45 @@ package com.shuwei.dish.match.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.widget.LinearLayout
|
||||
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.GlobalData
|
||||
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.utils.NetworkUtil
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
|
||||
/**
|
||||
* 子设备专属页面(小屏幕设备使用)
|
||||
* 仅展示本机秤的实时数据和主设备连接状态
|
||||
* 布局简洁,适配小屏幕
|
||||
* 22个秤和18个秤使用与 MasterScaleActivity 相同的网格布局,其余使用线性列表
|
||||
*/
|
||||
class SlaveActivity : BaseActivity() {
|
||||
|
||||
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>()
|
||||
private val adapter = ScaleAdapter(scaleList)
|
||||
/** 22个秤网格 adapter */
|
||||
private var scale22Adapter: Scale22GridAdapter? = null
|
||||
/** 18个秤网格 adapter */
|
||||
private var scale18Adapter: Scale18GridAdapter? = null
|
||||
|
||||
/** 已连接的主设备数量(有连接即代表主设备在线) */
|
||||
private var masterConnected = false
|
||||
@@ -41,23 +53,73 @@ class SlaveActivity : BaseActivity() {
|
||||
|
||||
binding.tvDeviceId.text = "设备:${GlobalData.deviceId}"
|
||||
binding.tvDeviceIp.text = "IP:${getLocalIpAddress()}"
|
||||
binding.rvScaleList.layoutManager = LinearLayoutManager(this)
|
||||
binding.rvScaleList.itemAnimator = null // 关闭默认动画,避免数据频繁更新时出现闪烁错乱
|
||||
binding.rvScaleList.adapter = adapter
|
||||
|
||||
setupScaleList()
|
||||
updateMasterStatus(ScaleServiceManager.isMasterConnected)
|
||||
listenLocalScales()
|
||||
listenMasterConnection()
|
||||
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 后重复打开本页
|
||||
*/
|
||||
private fun addBackKeyListener() {
|
||||
onBackPressedDispatcher.addCallback(this) {
|
||||
// 禁用返回键,不执行任何操作
|
||||
}
|
||||
onBackPressedDispatcher.addCallback(this) { }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,19 +133,39 @@ class SlaveActivity : BaseActivity() {
|
||||
|
||||
/**
|
||||
* 监听本机 WeightUtil 回调,实时刷新秤列表
|
||||
* 22/18个秤走网格 adapter,其余走线性列表
|
||||
*/
|
||||
private fun listenLocalScales() {
|
||||
WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
||||
runOnUiThread {
|
||||
val idx = scaleList.indexOfFirst { it.address == address }
|
||||
val item = ScaleItem(address, weight, state)
|
||||
if (idx >= 0) {
|
||||
scaleList[idx] = item
|
||||
adapter.notifyItemChanged(idx)
|
||||
} else {
|
||||
scaleList.add(item)
|
||||
scaleList.sortBy { it.address }
|
||||
adapter.notifyDataSetChanged()
|
||||
when {
|
||||
scale22Adapter != null -> {
|
||||
// 用最新数据更新缓存后整体刷新网格
|
||||
val existing = scale22Adapter!!.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)
|
||||
scale22Adapter!!.updateByAddress(existing)
|
||||
}
|
||||
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")
|
||||
fun updateMasterStatus(connected: Boolean) {
|
||||
@@ -112,37 +193,27 @@ class SlaveActivity : BaseActivity() {
|
||||
const val TAG = "SlaveActivity"
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出清零确认对话框
|
||||
* @param address 目标秤地址
|
||||
*/
|
||||
private fun showTareDialog(address: Int) {
|
||||
CommonDialog(this)
|
||||
.setTitle("清零确认")
|
||||
.setContent("确认对秤 $address 执行清零操作?")
|
||||
.setNegativeButton("取消")
|
||||
.setPositiveButton("确认") {
|
||||
WeightUtil.tareTwo(address)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本机局域网 IP,委托给 NetworkUtil 统一处理版本兼容
|
||||
* 获取失败时返回"未知"
|
||||
*/
|
||||
private fun getLocalIpAddress(): String {
|
||||
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.databinding.FragmentDeviceConfigBinding
|
||||
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.entity.SeasoningEntity
|
||||
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.viewmodel.AppViewModel
|
||||
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.SelectDishActivity
|
||||
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_marginStart="30dp"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingTop="30dp"
|
||||
android:paddingBottom="30dp"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="28sp"
|
||||
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_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp"
|
||||
android:paddingHorizontal="5dp"
|
||||
tools:background="@color/bg_color">
|
||||
|
||||
<!-- 本机IP地址 -->
|
||||
<TextView
|
||||
android:id="@+id/tvDeviceIp"
|
||||
android:layout_width="wrap_content"
|
||||
<!-- IP 与主设备状态同行显示 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="0dp"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/home_title"
|
||||
tools:text="IP:192.168.1.100" />
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<!-- 本机IP地址 -->
|
||||
<TextView
|
||||
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
|
||||
@@ -25,22 +41,10 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="0dp"
|
||||
android:textSize="18sp"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/home_sub_title"
|
||||
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
|
||||
android:id="@+id/rvScaleList"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="30dp"
|
||||
@@ -8,10 +7,29 @@
|
||||
android:background="@drawable/shape_white_30_corners"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/llContent"
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
@@ -20,7 +38,7 @@
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:layout_height="100dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
@@ -32,8 +50,7 @@
|
||||
android:gravity="center"
|
||||
android:background="@drawable/ripple_effect_light2"
|
||||
android:textColor="@color/dish_green"
|
||||
android:textSize="30sp"
|
||||
tools:text="取消" />
|
||||
android:textSize="30sp" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="1dp"
|
||||
@@ -49,8 +66,7 @@
|
||||
android:textColor="@color/dish_green"
|
||||
android:background="@drawable/ripple_effect_light2"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="确定" />
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -46,11 +46,22 @@
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivOperateIcon"
|
||||
android:layout_width="60dp"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
tools:src="@drawable/ic_dish_selected"
|
||||
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_constraintTop_toTopOf="parent"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
Reference in New Issue
Block a user