Compare commits
5
Commits
1ca25e6a1d
...
87bf11454c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87bf11454c | ||
|
|
768bde79a1 | ||
|
|
c61b1c8ef7 | ||
|
|
77df4a4d82 | ||
|
|
c35572dc41 |
@@ -93,8 +93,8 @@
|
||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||
android:screenOrientation="portrait"
|
||||
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
|
||||
<activity android:name="com.shuwei.dish.match.ui.DeviceConfigActivity"
|
||||
android:theme="@style/Theme.DishMatch.NoSplash" />
|
||||
<!-- <activity android:name="com.shuwei.dish.match.ui.DeviceConfigActivity"-->
|
||||
<!-- android:theme="@style/Theme.DishMatch.NoSplash" />-->
|
||||
<activity android:name="com.shuwei.dish.match.ui.SettingActivity"
|
||||
android:theme="@style/Theme.DishMatch.NoSplash"
|
||||
android:screenOrientation="portrait"
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.shuwei.dish.match.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.databinding.ListItemSeasoningCellBinding
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
|
||||
/**
|
||||
* 调料配置-下层(18格)网格 Adapter
|
||||
* 使用 GridLayoutManager(6列)排列,第1行格子 margin 较大,第2-3行 margin 较小
|
||||
* 实体类为 SeasoningEntity,格子显示调料名称 goodsName
|
||||
*/
|
||||
class Seasoning18GridAdapter : BaseQuickAdapter<SeasoningEntity, Seasoning18GridAdapter.VH>(mutableListOf()) {
|
||||
|
||||
/** 空位哨兵,sort=-1 表示无效 */
|
||||
private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 }
|
||||
|
||||
/** 当前高亮的格子位置,-1 表示无高亮 */
|
||||
private var highlightedPosition: Int = -1
|
||||
|
||||
/** 点击回调,预留,暂不使用 */
|
||||
var onItemClick: ((SeasoningEntity, Int) -> Unit)? = null
|
||||
|
||||
inner class VH(val b: ListItemSeasoningCellBinding) : QuickViewHolder(b.root)
|
||||
|
||||
/** 前6个位置为第1行(viewType=0,margin大),其余为第2-3行(viewType=1,margin小) */
|
||||
override fun getItemViewType(position: Int, list: List<SeasoningEntity>) =
|
||||
if (position < 6) 0 else 1
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val b = ListItemSeasoningCellBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
val margin = if (viewType == 0) 12.dp else 2.dp
|
||||
val screenWidth = parent.resources.displayMetrics.widthPixels
|
||||
val cellSize = (screenWidth - 6 * margin * 2 - 10.dp) / 6
|
||||
(b.root.layoutParams as? ViewGroup.MarginLayoutParams)?.also {
|
||||
it.width = cellSize
|
||||
it.height = cellSize
|
||||
// 第1行:顶部间距为0,底部间距2dp,左右保持原有 margin
|
||||
if (viewType == 0) it.setMargins(margin, 0, margin, 2.dp)
|
||||
else it.setMargins(margin, margin, margin, margin)
|
||||
}
|
||||
val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt()
|
||||
b.tvGoodsName.textSize = (baseSp * 0.18f).coerceIn(10f, 16f)
|
||||
val vh = VH(b)
|
||||
// 点击事件预留,暂不处理
|
||||
b.root.setOnClickListener {
|
||||
val pos = vh.bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
items.getOrNull(pos)?.let { item -> onItemClick?.invoke(item, pos) }
|
||||
}
|
||||
}
|
||||
return vh
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) {
|
||||
holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-"
|
||||
// 高亮格子:红底白字;其余恢复默认
|
||||
if (position == highlightedPosition) {
|
||||
holder.b.tvGoodsName.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
||||
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.white))
|
||||
} else {
|
||||
holder.b.tvGoodsName.setBackgroundResource(R.drawable.shape_scale_cell)
|
||||
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.home_title))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高亮指定位置的格子(红底白字),同时清除上一个高亮
|
||||
* @param position 要高亮的格子索引
|
||||
*/
|
||||
fun highlightPosition(position: Int) {
|
||||
val prev = highlightedPosition
|
||||
highlightedPosition = position
|
||||
if (prev >= 0) notifyItemChanged(prev)
|
||||
if (position >= 0) notifyItemChanged(position)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有高亮,恢复默认样式
|
||||
*/
|
||||
fun clearHighlight() {
|
||||
highlightPosition(-1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定位置的调料名称并刷新对应格子
|
||||
* @param position 格子索引
|
||||
* @param name 新的调料名称
|
||||
*/
|
||||
fun updateItemName(position: Int, name: String) {
|
||||
items.getOrNull(position)?.goodsName = name
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 SCALE_ORDER_18 的 sort 顺序更新数据,空位用哨兵填充
|
||||
*/
|
||||
fun update(seasoningList: List<SeasoningEntity>) {
|
||||
val bySort = seasoningList.associateBy { it.sort }
|
||||
val filled = ScaleDeviceConfig.SCALE_ORDER_18.map { sort ->
|
||||
bySort[sort] ?: empty.copy().also { it.sort = sort }
|
||||
}
|
||||
submitList(filled)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.shuwei.dish.match.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chad.library.adapter4.BaseQuickAdapter
|
||||
import com.chad.library.adapter4.viewholder.QuickViewHolder
|
||||
import com.google.android.flexbox.FlexboxLayoutManager
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.databinding.ListItemSeasoningCellBinding
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
|
||||
/**
|
||||
* 调料配置-上层(22格)网格 Adapter
|
||||
* 使用 FlexboxLayoutManager 按列排列,前4个格子较大,其余较小
|
||||
* 实体类为 SeasoningEntity,格子显示调料名称 goodsName
|
||||
*
|
||||
* @param largeSize 前4个大格子的尺寸(px)
|
||||
* @param smallSize 其余小格子的尺寸(px)
|
||||
*/
|
||||
class Seasoning22GridAdapter(
|
||||
private val largeSize: Int,
|
||||
private val smallSize: Int
|
||||
) : BaseQuickAdapter<SeasoningEntity, Seasoning22GridAdapter.VH>(mutableListOf()) {
|
||||
|
||||
/** 空位哨兵,sort=-1 表示无效 */
|
||||
private val empty = SeasoningEntity(goodsId = "").also { it.sort = -1 }
|
||||
|
||||
/** 当前高亮的格子位置,-1 表示无高亮 */
|
||||
private var highlightedPosition: Int = -1
|
||||
|
||||
/** 点击回调,预留,暂不使用 */
|
||||
var onItemClick: ((SeasoningEntity, Int) -> Unit)? = null
|
||||
|
||||
inner class VH(val b: ListItemSeasoningCellBinding) : QuickViewHolder(b.root)
|
||||
|
||||
/** 前4个位置为大格子(viewType=1),其余为小格子(viewType=0) */
|
||||
override fun getItemViewType(position: Int, list: List<SeasoningEntity>) =
|
||||
if (position < 4) 1 else 0
|
||||
|
||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||
val b = ListItemSeasoningCellBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
val 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.tvGoodsName.textSize = (baseSp * 0.18f).coerceIn(10f, 16f)
|
||||
val vh = VH(b)
|
||||
// 点击事件预留,暂不处理
|
||||
b.root.setOnClickListener {
|
||||
val pos = vh.bindingAdapterPosition
|
||||
if (pos != RecyclerView.NO_POSITION) {
|
||||
items.getOrNull(pos)?.let { item -> onItemClick?.invoke(item, pos) }
|
||||
}
|
||||
}
|
||||
return vh
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) {
|
||||
holder.b.tvGoodsName.text = item?.goodsName?.takeIf { it.isNotBlank() } ?: "-"
|
||||
// 高亮格子:红底白字;其余恢复默认
|
||||
if (position == highlightedPosition) {
|
||||
holder.b.tvGoodsName.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
||||
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.white))
|
||||
} else {
|
||||
holder.b.tvGoodsName.setBackgroundResource(R.drawable.shape_scale_cell)
|
||||
holder.b.tvGoodsName.setTextColor(ContextCompat.getColor(holder.b.root.context, R.color.home_title))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 高亮指定位置的格子(红底白字),同时清除上一个高亮
|
||||
* @param position 要高亮的格子索引
|
||||
*/
|
||||
fun highlightPosition(position: Int) {
|
||||
val prev = highlightedPosition
|
||||
highlightedPosition = position
|
||||
if (prev >= 0) notifyItemChanged(prev)
|
||||
if (position >= 0) notifyItemChanged(position)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有高亮,恢复默认样式
|
||||
*/
|
||||
fun clearHighlight() {
|
||||
highlightPosition(-1)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定位置的调料名称并刷新对应格子
|
||||
* @param position 格子索引
|
||||
* @param name 新的调料名称
|
||||
*/
|
||||
fun updateItemName(position: Int, name: String) {
|
||||
items.getOrNull(position)?.goodsName = name
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 SCALE_ORDER_22 的 sort 顺序更新数据,空位用哨兵填充
|
||||
*/
|
||||
fun update(seasoningList: List<SeasoningEntity>) {
|
||||
val bySort = seasoningList.associateBy { it.sort }
|
||||
val filled = ScaleDeviceConfig.SCALE_ORDER_22.map { sort ->
|
||||
bySort[sort] ?: empty.copy().also { it.sort = sort }
|
||||
}
|
||||
submitList(filled)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package com.shuwei.dish.match.dialog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.isEmpty
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.shuwei.dish.match.R
|
||||
import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
|
||||
import com.shuwei.dish.match.base.BaseActivity
|
||||
import com.shuwei.dish.match.databinding.DialogSeasoningSelectBinding
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
import com.shuwei.dish.match.utils.KeyboardUtil
|
||||
import com.shuwei.dish.match.utils.ext.addOnActionSearchListener
|
||||
import com.shuwei.dish.match.utils.ext.toast
|
||||
|
||||
/**
|
||||
* 调料选择弹窗,不含重量显示,继承 BottomSheetDialog 确保只初始化一次
|
||||
* @param activity 宿主 Activity
|
||||
* @param clickIndex 点击的调料槽位索引
|
||||
* @param onItemSelected 用户点击某一调料时的回调
|
||||
*/
|
||||
class SeasoningSelectDialog(
|
||||
private val activity: BaseActivity,
|
||||
private val clickIndex: Int,
|
||||
private val onItemSelected: (item: SeasoningEntity) -> Unit
|
||||
) : BottomSheetDialog(activity, R.style.BottomSheet) {
|
||||
|
||||
companion object {
|
||||
const val TAG = "SeasoningSelectDialog"
|
||||
}
|
||||
|
||||
private val binding = DialogSeasoningSelectBinding.inflate(LayoutInflater.from(activity))
|
||||
private val list = mutableListOf<SeasoningEntity>()
|
||||
private val adapter = SeasoningSearchAdapter(list).apply {
|
||||
setOnItemClickListener { _, _, position ->
|
||||
list[position].isClicked = true
|
||||
notifyItemChanged(position)
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
onItemSelected(list[position])
|
||||
dismiss()
|
||||
}, 300)
|
||||
}
|
||||
}
|
||||
|
||||
private var goodsName: String? = null
|
||||
private var pageNo = 1
|
||||
private val pageSize = 50
|
||||
|
||||
init {
|
||||
setContentView(binding.root)
|
||||
setCancelable(true)
|
||||
behavior.skipCollapsed = false
|
||||
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
|
||||
window?.setGravity(Gravity.BOTTOM)
|
||||
window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
window?.setWindowAnimations(R.style.DialogSoftInputAnimation)
|
||||
window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
|
||||
setOnDismissListener {
|
||||
activity.hideStatusBar()
|
||||
}
|
||||
|
||||
// 输入框:清空时重置列表
|
||||
binding.etSheetInput.run {
|
||||
hint = "输入调料名称"
|
||||
addTextChangedListener(
|
||||
onTextChanged = { text, _, _, _ -> goodsName = text.toString() },
|
||||
afterTextChanged = {
|
||||
if (it.isNullOrBlank()) {
|
||||
list.clear()
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
)
|
||||
addOnActionSearchListener { searchGoods(this) }
|
||||
}
|
||||
|
||||
// RecyclerView 初始化及滑动冲突处理
|
||||
binding.recyclerView.run {
|
||||
layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.VERTICAL, false)
|
||||
adapter = this@SeasoningSelectDialog.adapter
|
||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(rv, dx, dy)
|
||||
// 解决 RecyclerView 与 SmartRefreshLayout 滑动冲突
|
||||
val topRowVerticalPosition = if (rv.isEmpty()) 0 else rv.getChildAt(0).top
|
||||
binding.refreshLayout.setNestedScrollingEnabled(topRowVerticalPosition >= 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
binding.refreshLayout.run {
|
||||
setEnableRefresh(false)
|
||||
setEnableLoadMore(false)
|
||||
setOnRefreshListener { pageNo = 1; getGoodsList() }
|
||||
setOnLoadMoreListener { getGoodsList() }
|
||||
}
|
||||
|
||||
binding.ivSearch.setOnClickListener { searchGoods(it) }
|
||||
binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it.context, it) }
|
||||
}
|
||||
|
||||
/** 弹窗显示时自动加载默认调料列表 */
|
||||
override fun show() {
|
||||
super.show()
|
||||
pageNo = 1
|
||||
getGoodsList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发搜索:校验输入、隐藏键盘、发起请求
|
||||
*/
|
||||
private fun searchGoods(v: View) {
|
||||
if (goodsName.isNullOrBlank()) {
|
||||
activity.toast("请${binding.etSheetInput.hint}")
|
||||
return
|
||||
}
|
||||
pageNo = 1
|
||||
getGoodsList()
|
||||
KeyboardUtil.hideKeyboard(v.context, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求调料列表(临时使用本地模拟数据,正式联调时替换为真实接口)
|
||||
*/
|
||||
private fun getGoodsList() {
|
||||
// TODO: 联调时注释掉下方模拟数据,取消注释真实接口调用
|
||||
val mockData = mutableListOf(
|
||||
SeasoningEntity(goodsId = "1", goodsName = "精盐"),
|
||||
SeasoningEntity(goodsId = "2", goodsName = "鸡精"),
|
||||
SeasoningEntity(goodsId = "3", goodsName = "陈醋"),
|
||||
SeasoningEntity(goodsId = "4", goodsName = "老抽"),
|
||||
SeasoningEntity(goodsId = "5", goodsName = "生抽"),
|
||||
SeasoningEntity(goodsId = "6", goodsName = "花生油"),
|
||||
SeasoningEntity(goodsId = "7", goodsName = "菜籽油"),
|
||||
SeasoningEntity(goodsId = "8", goodsName = "胡椒粉"),
|
||||
SeasoningEntity(goodsId = "9", goodsName = "十三香"),
|
||||
SeasoningEntity(goodsId = "10", goodsName = "料酒"),
|
||||
SeasoningEntity(goodsId = "11", goodsName = "白醋")
|
||||
)
|
||||
loadGoodsList(mockData)
|
||||
|
||||
// val param = mutableMapOf<String, Any>(
|
||||
// "goodsType" to "1",
|
||||
// "placeId" to BaseApp.canteenId,
|
||||
// "pageNum" to pageNo,
|
||||
// "pageSize" to pageSize
|
||||
// )
|
||||
// if (!goodsName.isNullOrBlank()) {
|
||||
// param["goodsName"] = goodsName!!
|
||||
// }
|
||||
// activity.querySeasoningList(param = param, onSuccess = {
|
||||
// loadGoodsList(it)
|
||||
// }, onFailure = { _, msg ->
|
||||
// activity.toast(msg)
|
||||
// finishRefresh()
|
||||
// })
|
||||
}
|
||||
|
||||
/**
|
||||
* 将返回数据填充到列表
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun loadGoodsList(records: MutableList<SeasoningEntity>?) {
|
||||
finishRefresh()
|
||||
if (records.isNullOrEmpty()) {
|
||||
activity.toast("暂未搜索到调料信息")
|
||||
return
|
||||
}
|
||||
if (pageNo == 1) list.clear()
|
||||
list.addAll(records)
|
||||
adapter.notifyDataSetChanged()
|
||||
val isLoadMoreEnable = records.size >= pageSize
|
||||
binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable)
|
||||
if (isLoadMoreEnable) pageNo++
|
||||
}
|
||||
|
||||
private fun finishRefresh() {
|
||||
if (pageNo == 1) binding.refreshLayout.finishRefresh()
|
||||
else binding.refreshLayout.finishLoadMore()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.shuwei.dish.match.scale
|
||||
|
||||
/**
|
||||
* 秤事件数据类,用于子设备向主设备发送非重量类通知
|
||||
* @param type 事件类型,参见 companion object 中的常量
|
||||
* @param deviceId 发送方设备 ID
|
||||
* @param address 触发事件的秤地址
|
||||
* @param delta 重量变化量(克),仅 TYPE_SEASONING_ADDED 时有意义
|
||||
*/
|
||||
data class ScaleEvent(
|
||||
val type: String,
|
||||
val deviceId: String,
|
||||
val address: Int,
|
||||
val delta: Double = 0.0
|
||||
) {
|
||||
companion object {
|
||||
/** 调料添加事件:某秤重量增加超过阈值 */
|
||||
const val TYPE_SEASONING_ADDED = "seasoning_added"
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,26 @@ object ScaleServiceManager {
|
||||
val allScales: StateFlow<Map<String, ScaleData>>?
|
||||
get() = aggregator?.allScales
|
||||
|
||||
/**
|
||||
* 收到子设备秤事件(如调料添加)时的回调,仅主设备有效
|
||||
* 在子线程中调用,需自行切换到主线程更新 UI
|
||||
*/
|
||||
private var _onScaleEvent: ((ScaleEvent) -> Unit)? = null
|
||||
var onScaleEvent: ((ScaleEvent) -> Unit)?
|
||||
get() = _onScaleEvent
|
||||
set(value) {
|
||||
_onScaleEvent = value
|
||||
wsClient?.onScaleEvent = value
|
||||
}
|
||||
|
||||
/**
|
||||
* 向主设备广播秤事件(子设备调用)
|
||||
* @param event 要广播的事件
|
||||
*/
|
||||
fun broadcastEvent(event: ScaleEvent) {
|
||||
wsServer?.broadcastEvent(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* 主设备连接状态回调(仅子设备使用)
|
||||
* 设置后会立即转发给 wsServer;需在 start() 之后设置才能生效
|
||||
@@ -100,6 +120,7 @@ object ScaleServiceManager {
|
||||
// WebSocket 客户端
|
||||
val client = ScaleWebSocketClient().also {
|
||||
it.onScaleData = { data -> aggregator?.onRemoteScaleData(data) }
|
||||
it.onScaleEvent = { event -> _onScaleEvent?.invoke(event) }
|
||||
it.onDeviceDisconnected = { remoteId -> aggregator?.removeDevice(remoteId) }
|
||||
}
|
||||
wsClient = client
|
||||
|
||||
@@ -49,6 +49,9 @@ class ScaleWebSocketClient {
|
||||
/** 收到秤数据时的回调,在子线程调用 */
|
||||
var onScaleData: ((data: ScaleData) -> Unit)? = null
|
||||
|
||||
/** 收到秤事件(如调料添加通知)时的回调,在子线程调用 */
|
||||
var onScaleEvent: ((event: ScaleEvent) -> Unit)? = null
|
||||
|
||||
/** 设备断线时的回调(连接失败或关闭),在子线程调用 */
|
||||
var onDeviceDisconnected: ((deviceId: String) -> Unit)? = null
|
||||
|
||||
@@ -60,6 +63,8 @@ class ScaleWebSocketClient {
|
||||
*/
|
||||
fun connect(deviceId: String, host: String, port: Int) {
|
||||
removedDevices.remove(deviceId)
|
||||
// 取消旧 IP 的重连任务,防止 IP 变化时新旧任务并发竞争
|
||||
reconnectTasks.remove(deviceId)?.cancel(false)
|
||||
reconnectDelays[deviceId] = RECONNECT_BASE_MS
|
||||
doConnect(deviceId, host, port)
|
||||
}
|
||||
@@ -78,10 +83,17 @@ class ScaleWebSocketClient {
|
||||
|
||||
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||
try {
|
||||
// 通过 type 字段区分 ScaleEvent 与 ScaleData
|
||||
val raw = gson.fromJson(text, Map::class.java)
|
||||
if (raw.containsKey("type")) {
|
||||
val event = gson.fromJson(text, ScaleEvent::class.java)
|
||||
onScaleEvent?.invoke(event)
|
||||
} else {
|
||||
val data = gson.fromJson(text, ScaleData::class.java)
|
||||
onScaleData?.invoke(data)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "解析秤数据失败: ${e.message}")
|
||||
Log.w(TAG, "解析消息失败: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,14 @@ class ScaleWebSocketServer(private val deviceId: String, private val context: Co
|
||||
Log.d(TAG, "WebSocket 服务端已停止")
|
||||
}
|
||||
|
||||
/**
|
||||
* 向所有已连接的主设备广播秤事件(如调料添加通知)
|
||||
* @param event 要广播的事件
|
||||
*/
|
||||
fun broadcastEvent(event: ScaleEvent) {
|
||||
broadcast(gson.toJson(event))
|
||||
}
|
||||
|
||||
/**
|
||||
* 向所有已连接客户端广播消息
|
||||
*/
|
||||
|
||||
@@ -1,345 +1,347 @@
|
||||
package com.shuwei.dish.match.ui
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.AbsoluteSizeSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.LineHeightSpan
|
||||
import android.text.style.StyleSpan
|
||||
import android.util.Log
|
||||
import android.util.SparseArray
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.graphics.toColorInt
|
||||
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.db.AppRepository
|
||||
import com.shuwei.dish.match.dialog.CommonDialog
|
||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
import com.shuwei.dish.match.entity.ResetReasoningRecord
|
||||
import com.shuwei.dish.match.utils.AddressUtil
|
||||
import com.shuwei.dish.match.utils.JsonAssetsLoader
|
||||
import com.shuwei.dish.match.utils.MultiClickDetector
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.appendText
|
||||
import com.shuwei.dish.match.utils.ext.buildSpannableString
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import com.shuwei.dish.match.utils.ext.startActivity
|
||||
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.adapter.TextCellAdapter
|
||||
import com.shuwei.dish.match.databinding.ActivityDeviceConfigBinding
|
||||
import com.shuwei.dish.match.dialog.SeasoningSearchDialog
|
||||
import com.shuwei.dish.match.utils.SpTool
|
||||
import com.shuwei.dish.match.utils.ext.clickWithDebounce
|
||||
import com.shuwei.dish.match.utils.ext.gone
|
||||
|
||||
class DeviceConfigActivity : BaseActivity() {
|
||||
|
||||
companion object {
|
||||
|
||||
const val TAG = "DeviceConfigActivity"
|
||||
|
||||
const val COOK_MODE = "cookMode"
|
||||
|
||||
val modeList = listOf<ModeBean>(
|
||||
ModeBean("制作模式", "菜品快速制作,记录熟重"),
|
||||
ModeBean("采样模式", "菜品快速制作,记录熟重"),
|
||||
ModeBean("品控模式", "菜品快速制作,记录熟重")
|
||||
)
|
||||
}
|
||||
|
||||
data class ModeBean(
|
||||
var modeName: String,
|
||||
var modeDesc: String
|
||||
)
|
||||
|
||||
private lateinit var binding: ActivityDeviceConfigBinding
|
||||
|
||||
private lateinit var appViewModel: AppViewModel
|
||||
|
||||
private var cookMode: Int = 0
|
||||
// private val weightArray = SparseIntArray()
|
||||
private val weightArray = SparseArray<Double>()
|
||||
private val addressArray = AddressUtil.getWeighAddressArray()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityDeviceConfigBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
cookMode = intent.getIntExtra(COOK_MODE, 0)
|
||||
initViewModel()
|
||||
setHeaderBackground()
|
||||
setTitleBar(titleBarAction = {
|
||||
it.visible()
|
||||
}, titleAction = {
|
||||
it.text = "设备配置"
|
||||
}, rightIconActon = {
|
||||
it.gone()
|
||||
// it.alpha = 0.0F
|
||||
// it.setImageResource(R.drawable.ic_setting)
|
||||
// it.setOnClickListener {_->
|
||||
// detector.setOnDelayedMultiClickListener(it) {
|
||||
// defaultDataSettingDialog()
|
||||
//package com.shuwei.dish.match.ui
|
||||
//
|
||||
//import android.graphics.Typeface
|
||||
//import android.os.Bundle
|
||||
//import android.text.SpannableStringBuilder
|
||||
//import android.text.style.AbsoluteSizeSpan
|
||||
//import android.text.style.ForegroundColorSpan
|
||||
//import android.text.style.LineHeightSpan
|
||||
//import android.text.style.StyleSpan
|
||||
//import android.util.Log
|
||||
//import android.util.SparseArray
|
||||
//import android.widget.FrameLayout
|
||||
//import androidx.core.graphics.toColorInt
|
||||
//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.db.AppRepository
|
||||
//import com.shuwei.dish.match.dialog.CommonDialog
|
||||
//import com.shuwei.dish.match.entity.SeasoningEntity
|
||||
//import com.shuwei.dish.match.entity.ResetReasoningRecord
|
||||
//import com.shuwei.dish.match.utils.AddressUtil
|
||||
//import com.shuwei.dish.match.utils.JsonAssetsLoader
|
||||
//import com.shuwei.dish.match.utils.MultiClickDetector
|
||||
//import com.shuwei.dish.match.utils.WeightUtil
|
||||
//import com.shuwei.dish.match.utils.ext.appendText
|
||||
//import com.shuwei.dish.match.utils.ext.buildSpannableString
|
||||
//import com.shuwei.dish.match.utils.ext.dp
|
||||
//import com.shuwei.dish.match.utils.ext.startActivity
|
||||
//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.adapter.TextCellAdapter
|
||||
//import com.shuwei.dish.match.databinding.ActivityDeviceConfigBinding
|
||||
//import com.shuwei.dish.match.dialog.SeasoningSearchDialog
|
||||
//import com.shuwei.dish.match.utils.SpTool
|
||||
//import com.shuwei.dish.match.utils.ext.clickWithDebounce
|
||||
//import com.shuwei.dish.match.utils.ext.gone
|
||||
//import com.shuwei.dish.match.utils.ext.toJsonString
|
||||
//
|
||||
//class DeviceConfigActivity : BaseActivity() {
|
||||
//
|
||||
// companion object {
|
||||
//
|
||||
// const val TAG = "DeviceConfigActivity"
|
||||
//
|
||||
// const val COOK_MODE = "cookMode"
|
||||
//
|
||||
// val modeList = listOf<ModeBean>(
|
||||
// ModeBean("制作模式", "菜品快速制作,记录熟重"),
|
||||
// ModeBean("采样模式", "菜品快速制作,记录熟重"),
|
||||
// ModeBean("品控模式", "菜品快速制作,记录熟重")
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// data class ModeBean(
|
||||
// var modeName: String,
|
||||
// var modeDesc: String
|
||||
// )
|
||||
//
|
||||
// private lateinit var binding: ActivityDeviceConfigBinding
|
||||
//
|
||||
// private lateinit var appViewModel: AppViewModel
|
||||
//
|
||||
// private var cookMode: Int = 0
|
||||
//// private val weightArray = SparseIntArray()
|
||||
// private val weightArray = SparseArray<Double>()
|
||||
// private val addressArray = AddressUtil.getWeighAddressArray()
|
||||
//
|
||||
// override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// super.onCreate(savedInstanceState)
|
||||
// binding = ActivityDeviceConfigBinding.inflate(layoutInflater)
|
||||
// setContentView(binding.root)
|
||||
// cookMode = intent.getIntExtra(COOK_MODE, 0)
|
||||
// initViewModel()
|
||||
// setHeaderBackground()
|
||||
// setTitleBar(titleBarAction = {
|
||||
// it.visible()
|
||||
// }, titleAction = {
|
||||
// it.text = "设备配置"
|
||||
// }, rightIconActon = {
|
||||
// it.gone()
|
||||
//// it.alpha = 0.0F
|
||||
//// it.setImageResource(R.drawable.ic_setting)
|
||||
//// it.setOnClickListener {_->
|
||||
//// detector.setOnDelayedMultiClickListener(it) {
|
||||
//// defaultDataSettingDialog()
|
||||
//// }
|
||||
//// }
|
||||
// })
|
||||
//
|
||||
// loadQualitySpan(isEnable = false)
|
||||
// when (cookMode) {
|
||||
// 1 -> {
|
||||
// binding.rbModeCook.isChecked = true
|
||||
// binding.rbModeSampling.isChecked = false
|
||||
// loadCookSpan(isClicked = true)
|
||||
// loadSamplingSpan()
|
||||
// }
|
||||
//
|
||||
// 2 -> {
|
||||
// binding.rbModeCook.isChecked = false
|
||||
// binding.rbModeSampling.isChecked = true
|
||||
// loadCookSpan()
|
||||
// loadSamplingSpan(isClicked = true)
|
||||
// }
|
||||
//
|
||||
// else -> {
|
||||
// binding.rbModeCook.isChecked = true
|
||||
// binding.rbModeSampling.isChecked = false
|
||||
// loadCookSpan(isClicked = true)
|
||||
// loadSamplingSpan()
|
||||
// }
|
||||
// }
|
||||
})
|
||||
|
||||
loadQualitySpan(isEnable = false)
|
||||
when (cookMode) {
|
||||
1 -> {
|
||||
binding.rbModeCook.isChecked = true
|
||||
binding.rbModeSampling.isChecked = false
|
||||
loadCookSpan(isClicked = true)
|
||||
loadSamplingSpan()
|
||||
}
|
||||
|
||||
2 -> {
|
||||
binding.rbModeCook.isChecked = false
|
||||
binding.rbModeSampling.isChecked = true
|
||||
loadCookSpan()
|
||||
loadSamplingSpan(isClicked = true)
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.rbModeCook.isChecked = true
|
||||
binding.rbModeSampling.isChecked = false
|
||||
loadCookSpan(isClicked = true)
|
||||
loadSamplingSpan()
|
||||
}
|
||||
}
|
||||
|
||||
binding.rbModeCook.setOnClickListener {
|
||||
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 0)
|
||||
binding.rbModeCook.isChecked = true
|
||||
binding.rbModeSampling.isChecked = false
|
||||
loadCookSpan(isClicked = true)
|
||||
loadSamplingSpan()
|
||||
startActivity<SelectDishActivity> {
|
||||
putExtra(SelectDishActivity.IS_CONFIG_PAGE, true)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
binding.rbModeSampling.setOnClickListener {
|
||||
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 1)
|
||||
binding.rbModeCook.isChecked = false
|
||||
binding.rbModeSampling.isChecked = true
|
||||
loadCookSpan()
|
||||
loadSamplingSpan(isClicked = true)
|
||||
startActivity<SamplingListActivity> {
|
||||
putExtra(SamplingListActivity.IS_CONFIG_PAGE, true) }
|
||||
finish()
|
||||
}
|
||||
addWeighListener()
|
||||
addGridItemListener()
|
||||
loadSeasoning()
|
||||
}
|
||||
|
||||
private fun addWeighListener() {
|
||||
WeightUtil.addWeightListener(
|
||||
weightKey = TAG,
|
||||
getWeight = { address, state, weight ->
|
||||
Log.d(TAG, "addWeighListener: address=$address,stat=$state,weight=$weight")
|
||||
weightArray.put(address, weight)
|
||||
val item = seasoningItems.firstOrNull { address == addressArray[it.sort] }
|
||||
item?.let {
|
||||
it.useWeight = weight.toDouble()
|
||||
updateGridData(it)
|
||||
}
|
||||
Log.d(TAG, "addWeighListener: update-----------------------")
|
||||
})
|
||||
}
|
||||
|
||||
private var seasoningItems = mutableListOf<SeasoningEntity>()
|
||||
private fun addGridItemListener() {
|
||||
binding.include.root.forEach { child ->
|
||||
child.clickWithDebounce {
|
||||
val sort = child.tag.toString().toInt()
|
||||
val entity = seasoningItems.firstOrNull { it.sort == sort } ?: SeasoningEntity()
|
||||
clickGridItem(sort, entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val detector = MultiClickDetector(targetCount = 10, intervalMs = 800)
|
||||
|
||||
private fun defaultDataSettingDialog() {
|
||||
CommonDialog(this)
|
||||
.setTitle("温馨提示")
|
||||
.setContent("您好,使用默认配置作为您的调料数据,则已有配置将被清除,确认吗?")
|
||||
.setNegativeButton("取消")
|
||||
.setPositiveButton("确认") { resetList() }
|
||||
.setOnDismissCallback { hideStatusBar() }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun resetList() {
|
||||
runCatching {
|
||||
val record = JsonAssetsLoader.parseJsonFromAssets<ResetReasoningRecord>(
|
||||
this,
|
||||
"default_seasoning_list.json"
|
||||
)
|
||||
// val weightArray = WeightUtil.weightArray
|
||||
appViewModel.clearAllSeasoning {
|
||||
record?.list?.forEach { entity ->
|
||||
entity.useWeight = weightArray[addressArray[entity.sort]].toDouble()
|
||||
appViewModel.saveSeasoning(entity) {
|
||||
updateGridData(entity)
|
||||
}
|
||||
}
|
||||
//
|
||||
// binding.rbModeCook.setOnClickListener {
|
||||
// SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 0)
|
||||
// binding.rbModeCook.isChecked = true
|
||||
// binding.rbModeSampling.isChecked = false
|
||||
// loadCookSpan(isClicked = true)
|
||||
// loadSamplingSpan()
|
||||
// startActivity<SelectDishActivity> {
|
||||
// putExtra(SelectDishActivity.IS_CONFIG_PAGE, true)
|
||||
// }
|
||||
// finish()
|
||||
// }
|
||||
// binding.rbModeSampling.setOnClickListener {
|
||||
// SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 1)
|
||||
// binding.rbModeCook.isChecked = false
|
||||
// binding.rbModeSampling.isChecked = true
|
||||
// loadCookSpan()
|
||||
// loadSamplingSpan(isClicked = true)
|
||||
// startActivity<SamplingListActivity> {
|
||||
// putExtra(SamplingListActivity.IS_CONFIG_PAGE, true) }
|
||||
// finish()
|
||||
// }
|
||||
// addWeighListener()
|
||||
// addGridItemListener()
|
||||
// loadSeasoning()
|
||||
}
|
||||
}.onFailure { it.printStackTrace() }
|
||||
}
|
||||
|
||||
private fun initViewModel() {
|
||||
val db = BaseApp.instance!!.database
|
||||
val factory =
|
||||
AppFactory(AppRepository(db.appDao()))
|
||||
appViewModel =
|
||||
ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
}
|
||||
|
||||
private fun loadSeasoning() {
|
||||
appViewModel.loadSeasoning {
|
||||
seasoningItems.clear()
|
||||
seasoningItems.addAll(it)
|
||||
initConfigData()
|
||||
setGridData(seasoningItems)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initConfigData() {
|
||||
repeat(12) { num ->
|
||||
val firstOne = seasoningItems.firstOrNull { it.sort == num }
|
||||
if (firstOne == null) {
|
||||
seasoningItems.add(SeasoningEntity().also { it.sort = num })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setGridData(list: MutableList<SeasoningEntity>) {
|
||||
val gridLayout = binding.include.root
|
||||
list.forEach { entity ->
|
||||
val tag = entity.sort.toString()
|
||||
val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateGridData(entity: SeasoningEntity) {
|
||||
val gridLayout = binding.include.root
|
||||
val tag = entity.sort.toString()
|
||||
val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
}
|
||||
|
||||
private fun clickGridItem(sort: Int, entity: SeasoningEntity) {
|
||||
val currentAddress = AddressUtil.getWeighAddressArray().get(sort)
|
||||
// val seasoningEntity = list.first{it.sort == sort}
|
||||
SeasoningSearchDialog(
|
||||
activity = this,
|
||||
weighIndex = sort,
|
||||
weighAddress = currentAddress,
|
||||
clickName = entity.goodsName
|
||||
) { item ->
|
||||
syncItem(entity, item)
|
||||
//3-调料
|
||||
entity.materialType = 3
|
||||
entity.sort = sort
|
||||
saveSeasoning(entity) {
|
||||
val tag = sort.toString()
|
||||
val gridLayout = binding.include.root
|
||||
val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
|
||||
private fun syncItem(oldItem: SeasoningEntity, newItem: SeasoningEntity) {
|
||||
oldItem.run {
|
||||
goodsId = newItem.goodsId
|
||||
goodsName = newItem.goodsName
|
||||
goodsOrRelationCode = newItem.goodsOrRelationCode
|
||||
relateionType = newItem.relateionType
|
||||
materialType = newItem.materialType
|
||||
allEdible = newItem.allEdible
|
||||
useWeight = newItem.useWeight
|
||||
popularName = newItem.popularName
|
||||
canteenId = newItem.canteenId
|
||||
relateionType_dictText = newItem.relateionType_dictText
|
||||
|
||||
foodId = newItem.foodId
|
||||
sort = newItem.sort
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadQualitySpan(isClicked: Boolean = false, isEnable: Boolean = true) {
|
||||
binding.rbModeQuality.text =
|
||||
getModeTextSpan(
|
||||
topText = modeList[2].modeName,
|
||||
bottomText = modeList[2].modeDesc,
|
||||
topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4",
|
||||
bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4"
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadSamplingSpan(isClicked: Boolean = false, isEnable: Boolean = true) {
|
||||
binding.rbModeSampling.text =
|
||||
getModeTextSpan(
|
||||
topText = modeList[1].modeName,
|
||||
bottomText = modeList[1].modeDesc,
|
||||
topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4",
|
||||
bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4"
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadCookSpan(isClicked: Boolean = false, isEnable: Boolean = true) {
|
||||
binding.rbModeCook.text =
|
||||
getModeTextSpan(
|
||||
topText = modeList[0].modeName,
|
||||
bottomText = modeList[0].modeDesc,
|
||||
topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4",
|
||||
bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4"
|
||||
)
|
||||
}
|
||||
|
||||
private fun getModeTextSpan(
|
||||
topText: String,
|
||||
topColor: String,
|
||||
bottomText: String,
|
||||
bottomColor: String
|
||||
): SpannableStringBuilder {
|
||||
|
||||
|
||||
return buildSpannableString {
|
||||
appendText(
|
||||
topText,
|
||||
ForegroundColorSpan(topColor.toColorInt()),
|
||||
StyleSpan(Typeface.BOLD),
|
||||
AbsoluteSizeSpan(36, true),
|
||||
LineHeightSpan { text, start, end, spanstartv, v, fm ->
|
||||
fm.descent += 10.dp // 增加行间距
|
||||
}
|
||||
)
|
||||
append("\n")
|
||||
appendText(
|
||||
bottomText,
|
||||
ForegroundColorSpan(bottomColor.toColorInt()),
|
||||
AbsoluteSizeSpan(26, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun saveSeasoning(entity: SeasoningEntity, action: () -> Unit = {}) {
|
||||
appViewModel.saveSeasoning(entity) {
|
||||
action()
|
||||
toast("保存成功")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// }
|
||||
//
|
||||
// private fun addWeighListener() {
|
||||
// WeightUtil.addWeightListener(
|
||||
// weightKey = TAG,
|
||||
// getWeight = { address, state, weight ->
|
||||
// Log.d(TAG, "addWeighListener: address=$address,stat=$state,weight=$weight")
|
||||
// weightArray.put(address, weight)
|
||||
// val item = seasoningItems.firstOrNull { address == addressArray[it.sort] }
|
||||
// item?.let {
|
||||
// it.useWeight = weight.toDouble()
|
||||
// updateGridData(it)
|
||||
// }
|
||||
// Log.d(TAG, "addWeighListener: update-----------------------")
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// private var seasoningItems = mutableListOf<SeasoningEntity>()
|
||||
// private fun addGridItemListener() {
|
||||
// binding.include.root.forEach { child ->
|
||||
// child.clickWithDebounce {
|
||||
// val sort = child.tag.toString().toInt()
|
||||
// val entity = seasoningItems.firstOrNull { it.sort == sort } ?: SeasoningEntity()
|
||||
// clickGridItem(sort, entity)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// val detector = MultiClickDetector(targetCount = 10, intervalMs = 800)
|
||||
//
|
||||
// private fun defaultDataSettingDialog() {
|
||||
// CommonDialog(this)
|
||||
// .setTitle("温馨提示")
|
||||
// .setContent("您好,使用默认配置作为您的调料数据,则已有配置将被清除,确认吗?")
|
||||
// .setNegativeButton("取消")
|
||||
// .setPositiveButton("确认") { resetList() }
|
||||
// .setOnDismissCallback { hideStatusBar() }
|
||||
// .show()
|
||||
// }
|
||||
//
|
||||
// private fun resetList() {
|
||||
// runCatching {
|
||||
// val record = JsonAssetsLoader.parseJsonFromAssets<ResetReasoningRecord>(
|
||||
// this,
|
||||
// "default_seasoning_list.json"
|
||||
// )
|
||||
//// val weightArray = WeightUtil.weightArray
|
||||
// appViewModel.clearAllSeasoning {
|
||||
// record?.list?.forEach { entity ->
|
||||
// entity.useWeight = weightArray[addressArray[entity.sort]].toDouble()
|
||||
// appViewModel.saveSeasoning(entity) {
|
||||
// updateGridData(entity)
|
||||
// }
|
||||
// }
|
||||
//// loadSeasoning()
|
||||
// }
|
||||
// }.onFailure { it.printStackTrace() }
|
||||
// }
|
||||
//
|
||||
// private fun initViewModel() {
|
||||
// val db = BaseApp.instance!!.database
|
||||
// val factory =
|
||||
// AppFactory(AppRepository(db.appDao()))
|
||||
// appViewModel =
|
||||
// ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
// }
|
||||
//
|
||||
// private fun loadSeasoning() {
|
||||
// appViewModel.loadSeasoning {
|
||||
// seasoningItems.clear()
|
||||
// seasoningItems.addAll(it)
|
||||
// initConfigData()
|
||||
// setGridData(seasoningItems)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun initConfigData() {
|
||||
// repeat(12) { num ->
|
||||
// val firstOne = seasoningItems.firstOrNull { it.sort == num }
|
||||
// if (firstOne == null) {
|
||||
// seasoningItems.add(SeasoningEntity().also { it.sort = num })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun setGridData(list: MutableList<SeasoningEntity>) {
|
||||
// Log.d(TAG, "setGridData: ${list.toJsonString()}")
|
||||
// val gridLayout = binding.include.root
|
||||
// list.forEach { entity ->
|
||||
// val tag = entity.sort.toString()
|
||||
// val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
// TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun updateGridData(entity: SeasoningEntity) {
|
||||
// val gridLayout = binding.include.root
|
||||
// val tag = entity.sort.toString()
|
||||
// val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
// TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
// }
|
||||
//
|
||||
// private fun clickGridItem(sort: Int, entity: SeasoningEntity) {
|
||||
// val currentAddress = AddressUtil.getWeighAddressArray().get(sort)
|
||||
//// val seasoningEntity = list.first{it.sort == sort}
|
||||
// SeasoningSearchDialog(
|
||||
// activity = this,
|
||||
// weighIndex = sort,
|
||||
// weighAddress = currentAddress,
|
||||
// clickName = entity.goodsName
|
||||
// ) { item ->
|
||||
// syncItem(entity, item)
|
||||
// //3-调料
|
||||
// entity.materialType = 3
|
||||
// entity.sort = sort
|
||||
// saveSeasoning(entity) {
|
||||
// val tag = sort.toString()
|
||||
// val gridLayout = binding.include.root
|
||||
// val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
// TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
// }
|
||||
// }.show()
|
||||
// }
|
||||
//
|
||||
// private fun syncItem(oldItem: SeasoningEntity, newItem: SeasoningEntity) {
|
||||
// oldItem.run {
|
||||
// goodsId = newItem.goodsId
|
||||
// goodsName = newItem.goodsName
|
||||
// goodsOrRelationCode = newItem.goodsOrRelationCode
|
||||
// relateionType = newItem.relateionType
|
||||
// materialType = newItem.materialType
|
||||
// allEdible = newItem.allEdible
|
||||
// useWeight = newItem.useWeight
|
||||
// popularName = newItem.popularName
|
||||
// canteenId = newItem.canteenId
|
||||
// relateionType_dictText = newItem.relateionType_dictText
|
||||
//
|
||||
// foodId = newItem.foodId
|
||||
// sort = newItem.sort
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun loadQualitySpan(isClicked: Boolean = false, isEnable: Boolean = true) {
|
||||
// binding.rbModeQuality.text =
|
||||
// getModeTextSpan(
|
||||
// topText = modeList[2].modeName,
|
||||
// bottomText = modeList[2].modeDesc,
|
||||
// topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4",
|
||||
// bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4"
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// private fun loadSamplingSpan(isClicked: Boolean = false, isEnable: Boolean = true) {
|
||||
// binding.rbModeSampling.text =
|
||||
// getModeTextSpan(
|
||||
// topText = modeList[1].modeName,
|
||||
// bottomText = modeList[1].modeDesc,
|
||||
// topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4",
|
||||
// bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4"
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// private fun loadCookSpan(isClicked: Boolean = false, isEnable: Boolean = true) {
|
||||
// binding.rbModeCook.text =
|
||||
// getModeTextSpan(
|
||||
// topText = modeList[0].modeName,
|
||||
// bottomText = modeList[0].modeDesc,
|
||||
// topColor = if (isEnable) if (isClicked) "#00BC71" else "#000000" else "#B4B4B4",
|
||||
// bottomColor = if (isEnable) "#8B8B8B" else "#B4B4B4"
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// private fun getModeTextSpan(
|
||||
// topText: String,
|
||||
// topColor: String,
|
||||
// bottomText: String,
|
||||
// bottomColor: String
|
||||
// ): SpannableStringBuilder {
|
||||
//
|
||||
//
|
||||
// return buildSpannableString {
|
||||
// appendText(
|
||||
// topText,
|
||||
// ForegroundColorSpan(topColor.toColorInt()),
|
||||
// StyleSpan(Typeface.BOLD),
|
||||
// AbsoluteSizeSpan(36, true),
|
||||
// LineHeightSpan { text, start, end, spanstartv, v, fm ->
|
||||
// fm.descent += 10.dp // 增加行间距
|
||||
// }
|
||||
// )
|
||||
// append("\n")
|
||||
// appendText(
|
||||
// bottomText,
|
||||
// ForegroundColorSpan(bottomColor.toColorInt()),
|
||||
// AbsoluteSizeSpan(26, true)
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun saveSeasoning(entity: SeasoningEntity, action: () -> Unit = {}) {
|
||||
// appViewModel.saveSeasoning(entity) {
|
||||
// action()
|
||||
// toast("保存成功")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
@@ -107,7 +107,7 @@ class HomeActivity : BaseActivity() {
|
||||
modeDesc = "菜品快速制作,记录熟重",
|
||||
modeIconId = R.drawable.ic_mode_cook,
|
||||
onClick = {
|
||||
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 0)
|
||||
SpTool.cookMode = 0
|
||||
startActivity<SelectDishActivity>()
|
||||
finish()
|
||||
})
|
||||
@@ -118,7 +118,7 @@ class HomeActivity : BaseActivity() {
|
||||
modeDesc = "菜品快速制作,记录熟重",
|
||||
modeIconId = R.drawable.ic_mode_sampling,
|
||||
onClick = {
|
||||
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 1)
|
||||
SpTool.cookMode = 1
|
||||
goSampling()
|
||||
})
|
||||
)
|
||||
|
||||
@@ -83,8 +83,7 @@ class InitActivity : BaseActivity() {
|
||||
return@launch
|
||||
}
|
||||
// 主设备:走原有路由逻辑
|
||||
val launchPageType = SpTool.getInt(SpTool.LAUNCH_PAGE_TYPE, -1)
|
||||
when (launchPageType) {
|
||||
when (SpTool.cookMode) {
|
||||
0 -> {
|
||||
startActivity<SelectDishActivity>()
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ package com.shuwei.dish.match.ui
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.AppCompatButton
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.fragment.app.Fragment
|
||||
@@ -20,6 +22,7 @@ import com.shuwei.dish.match.databinding.ActivitySettingBinding
|
||||
import com.shuwei.dish.match.databinding.LayoutCameraPreviewBinding
|
||||
import com.shuwei.dish.match.ui.fragment.CollectFragment
|
||||
import com.shuwei.dish.match.ui.fragment.DeviceConfigFragment
|
||||
import com.shuwei.dish.match.ui.fragment.SeasoningConfigFragment
|
||||
import com.shuwei.dish.match.utils.CameraUtils
|
||||
import com.shuwei.dish.match.utils.SpTool
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
@@ -72,18 +75,29 @@ class SettingActivity : BaseActivity() {
|
||||
// 设备配置按钮点击事件
|
||||
binding.btnDeviceConfig.setOnClickListener {
|
||||
showDeviceConfigFragment()
|
||||
updateButtonColors(isDeviceConfigSelected = true)
|
||||
|
||||
loadTabStyle(button = binding.btnDeviceConfig, isSelected = true)
|
||||
loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false)
|
||||
loadTabStyle(button = binding.btnFoodCollect, isSelected = false)
|
||||
}
|
||||
|
||||
// 调料配置按钮点击事件
|
||||
binding.btnSeasoningConfig.setOnClickListener {
|
||||
showSeasoningConfigFragment()
|
||||
|
||||
loadTabStyle(button = binding.btnDeviceConfig, isSelected = false)
|
||||
loadTabStyle(button = binding.btnSeasoningConfig, isSelected = true)
|
||||
loadTabStyle(button = binding.btnFoodCollect, isSelected = false)
|
||||
}
|
||||
|
||||
// 菜品采集按钮点击事件
|
||||
binding.btnFoodCollect.setOnClickListener {
|
||||
checkCameraPermissionAndShowFragment()
|
||||
updateButtonColors(isDeviceConfigSelected = false)
|
||||
}
|
||||
|
||||
// // 设备角色只读展示,角色由设备 ID 决定,不可手动切换
|
||||
// updateRoleButtonText()
|
||||
// binding.btnDeviceRole.isEnabled = false
|
||||
loadTabStyle(button = binding.btnDeviceConfig, isSelected = false)
|
||||
loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false)
|
||||
loadTabStyle(button = binding.btnFoodCollect, isSelected = true)
|
||||
}
|
||||
|
||||
// 主设备显示「秤数据监控」入口
|
||||
if (GlobalData.deviceRole == DeviceRole.MASTER) {
|
||||
@@ -94,7 +108,10 @@ class SettingActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
// 初始化按钮颜色(默认设备配置为选中状态)
|
||||
updateButtonColors(isDeviceConfigSelected = true)
|
||||
|
||||
loadTabStyle(button = binding.btnDeviceConfig, isSelected = true)
|
||||
loadTabStyle(button = binding.btnSeasoningConfig, isSelected = false)
|
||||
loadTabStyle(button = binding.btnFoodCollect, isSelected = false)
|
||||
}
|
||||
|
||||
// /** 刷新角色按钮文字 */
|
||||
@@ -104,43 +121,26 @@ class SettingActivity : BaseActivity() {
|
||||
// }
|
||||
|
||||
/**
|
||||
* 更新底部菜单按钮的颜色状态
|
||||
* @param isDeviceConfigSelected 设备配置按钮是否被选中
|
||||
* 加载顶部Tab按钮样式
|
||||
*
|
||||
* @param button 按钮
|
||||
* @param isSelected 是否选中
|
||||
*/
|
||||
private fun updateButtonColors(isDeviceConfigSelected: Boolean) {
|
||||
val selectedColor = "#FFFFFF".toColorInt() // 选中颜色(白色)
|
||||
val unselectedColor = "#5E7585".toColorInt() // 未选中颜色(灰色)
|
||||
|
||||
if (isDeviceConfigSelected) {
|
||||
// 设备配置按钮:选中状态
|
||||
binding.btnDeviceConfig.apply {
|
||||
setTextColor(selectedColor)
|
||||
private fun loadTabStyle(button: AppCompatButton, isSelected: Boolean) {
|
||||
button.apply {
|
||||
if (isSelected){
|
||||
// 选中
|
||||
setTextColor(Color.WHITE)
|
||||
textSize = 28f
|
||||
setTypeface(null, android.graphics.Typeface.BOLD)
|
||||
background = ContextCompat.getDrawable(this@SettingActivity, R.drawable.bg_btn_underline)
|
||||
}
|
||||
// 采集食材按钮:未选中状态
|
||||
binding.btnFoodCollect.apply {
|
||||
setTextColor(unselectedColor)
|
||||
textSize = 26f
|
||||
setTypeface(null, android.graphics.Typeface.NORMAL)
|
||||
background = null
|
||||
}
|
||||
} else {
|
||||
// 设备配置按钮:未选中状态
|
||||
binding.btnDeviceConfig.apply {
|
||||
setTextColor(unselectedColor)
|
||||
// 未选中
|
||||
setTextColor("#5E7585".toColorInt())
|
||||
textSize = 26f
|
||||
setTypeface(null, android.graphics.Typeface.NORMAL)
|
||||
background = null
|
||||
}
|
||||
// 采集食材按钮:选中状态
|
||||
binding.btnFoodCollect.apply {
|
||||
setTextColor(selectedColor)
|
||||
textSize = 28f
|
||||
setTypeface(null, android.graphics.Typeface.BOLD)
|
||||
background = ContextCompat.getDrawable(this@SettingActivity, R.drawable.bg_btn_underline)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,9 +149,10 @@ class SettingActivity : BaseActivity() {
|
||||
*/
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
private fun checkCameraPermissionAndShowFragment() {
|
||||
val collectFragment = fragmentList[2]
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
// Android 6.0 以下,直接显示 Fragment
|
||||
showFragment(fragmentList[1])
|
||||
showFragment(collectFragment)
|
||||
return
|
||||
}
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
@@ -160,13 +161,13 @@ class SettingActivity : BaseActivity() {
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// 权限已授予,直接显示 Fragment
|
||||
showFragment(fragmentList[1])
|
||||
showFragment(collectFragment)
|
||||
} else {
|
||||
// 权限未授予,申请权限
|
||||
requestPermission(Manifest.permission.CAMERA) { isGranted ->
|
||||
if (isGranted) {
|
||||
// 权限已授予,显示 CollectFragment
|
||||
showFragment(fragmentList[1])
|
||||
showFragment(collectFragment)
|
||||
} else {
|
||||
// 权限被拒绝
|
||||
toast("暂无相机权限,无法使用菜品采集功能")
|
||||
@@ -216,19 +217,22 @@ class SettingActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示设备配置Fragment
|
||||
* 显示设备切换Fragment
|
||||
*/
|
||||
private fun showDeviceConfigFragment() {
|
||||
// val fragment = DeviceConfigFragment.newInstance()
|
||||
// supportFragmentManager.beginTransaction()
|
||||
// .replace(binding.fragmentContainer.id, fragment)
|
||||
// .addToBackStack(null)
|
||||
// .commit()
|
||||
showFragment(fragmentList[0])
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示设调料配置Fragment
|
||||
*/
|
||||
private fun showSeasoningConfigFragment() {
|
||||
showFragment(fragmentList[1])
|
||||
}
|
||||
|
||||
private val fragmentList = mutableListOf<Fragment>().apply {
|
||||
add(DeviceConfigFragment.newInstance())
|
||||
add(DeviceConfigFragment())
|
||||
add(SeasoningConfigFragment())
|
||||
add(CollectFragment())
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
): FragmentCollectBinding {
|
||||
return FragmentCollectBinding.inflate(LayoutInflater.from(context))
|
||||
return FragmentCollectBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
//typealias CameraCallback = (Uri) -> Unit
|
||||
|
||||
@@ -51,21 +51,11 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
|
||||
companion object {
|
||||
const val TAG = "DeviceConfigFragment"
|
||||
const val COOK_MODE = "cookMode"
|
||||
|
||||
val modeList = listOf<ModeBean>(
|
||||
ModeBean("制作模式", "菜品快速制作,记录熟重"),
|
||||
ModeBean("采样模式", "菜品快速制作,记录熟重"),
|
||||
ModeBean("品控模式", "菜品快速制作,记录熟重")
|
||||
)
|
||||
|
||||
fun newInstance(cookMode: Int = 0): DeviceConfigFragment {
|
||||
return DeviceConfigFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putInt(COOK_MODE, cookMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ModeBean(
|
||||
@@ -73,9 +63,8 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
var modeDesc: String
|
||||
)
|
||||
|
||||
private lateinit var appViewModel: AppViewModel
|
||||
// private lateinit var appViewModel: AppViewModel
|
||||
|
||||
private var cookMode: Int = 0
|
||||
private val weightArray = SparseArray<Double>()
|
||||
private val addressArray = AddressUtil.getWeighAddressArray()
|
||||
private var seasoningItems = mutableListOf<SeasoningEntity>()
|
||||
@@ -89,21 +78,20 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
}
|
||||
|
||||
override fun initialize(){
|
||||
cookMode = arguments?.getInt(COOK_MODE, 0) ?: 0
|
||||
initViewModel()
|
||||
initUI()
|
||||
addWeighListener()
|
||||
addGridItemListener()
|
||||
loadSeasoning()
|
||||
// addWeighListener()
|
||||
// addGridItemListener()
|
||||
// loadSeasoning()
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化ViewModel
|
||||
*/
|
||||
private fun initViewModel() {
|
||||
val db = BaseApp.instance!!.database
|
||||
val factory = AppFactory(AppRepository(db.appDao()))
|
||||
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
// val db = BaseApp.instance!!.database
|
||||
// val factory = AppFactory(AppRepository(db.appDao()))
|
||||
// appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,15 +99,15 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
*/
|
||||
private fun initUI() {
|
||||
loadQualitySpan(isEnable = false)
|
||||
when (cookMode) {
|
||||
1 -> {
|
||||
when (SpTool.cookMode) {
|
||||
0 -> {
|
||||
binding.rbModeCook.isChecked = true
|
||||
binding.rbModeSampling.isChecked = false
|
||||
loadCookSpan(isClicked = true)
|
||||
loadSamplingSpan()
|
||||
}
|
||||
|
||||
2 -> {
|
||||
1 -> {
|
||||
binding.rbModeCook.isChecked = false
|
||||
binding.rbModeSampling.isChecked = true
|
||||
loadCookSpan()
|
||||
@@ -135,7 +123,7 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
}
|
||||
|
||||
binding.rbModeCook.setOnClickListener {
|
||||
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 0)
|
||||
SpTool.cookMode = 0
|
||||
binding.rbModeCook.isChecked = true
|
||||
binding.rbModeSampling.isChecked = false
|
||||
loadCookSpan(isClicked = true)
|
||||
@@ -146,7 +134,7 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
activity?.finish()
|
||||
}
|
||||
binding.rbModeSampling.setOnClickListener {
|
||||
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 1)
|
||||
SpTool.cookMode = 1
|
||||
binding.rbModeCook.isChecked = false
|
||||
binding.rbModeSampling.isChecked = true
|
||||
loadCookSpan()
|
||||
@@ -156,138 +144,138 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
activity?.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加称重监听
|
||||
*/
|
||||
private fun addWeighListener() {
|
||||
WeightUtil.addWeightListener(
|
||||
weightKey = TAG,
|
||||
getWeight = { address, state, weight ->
|
||||
Log.d(TAG, "addWeighListener: address=$address,stat=$state,weight=$weight")
|
||||
weightArray.put(address, weight)
|
||||
val item = seasoningItems.firstOrNull { address == addressArray[it.sort] }
|
||||
item?.let {
|
||||
it.useWeight = weight.toDouble()
|
||||
updateGridData(it)
|
||||
}
|
||||
Log.d(TAG, "addWeighListener: update-----------------------")
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加网格项点击监听
|
||||
*/
|
||||
private fun addGridItemListener() {
|
||||
binding.include.root.forEach { child ->
|
||||
child.clickWithDebounce {
|
||||
val sort = child.tag.toString().toInt()
|
||||
val entity = seasoningItems.firstOrNull { it.sort == sort } ?: SeasoningEntity()
|
||||
clickGridItem(sort, entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载调料数据
|
||||
*/
|
||||
private fun loadSeasoning() {
|
||||
appViewModel.loadSeasoning {
|
||||
seasoningItems.clear()
|
||||
seasoningItems.addAll(it)
|
||||
initConfigData()
|
||||
setGridData(seasoningItems)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置数据,确保有12个调料位置
|
||||
*/
|
||||
private fun initConfigData() {
|
||||
repeat(12) { num ->
|
||||
val firstOne = seasoningItems.firstOrNull { it.sort == num }
|
||||
if (firstOne == null) {
|
||||
seasoningItems.add(SeasoningEntity().also { it.sort = num })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置网格数据
|
||||
*/
|
||||
private fun setGridData(list: MutableList<SeasoningEntity>) {
|
||||
val gridLayout = binding.include.root
|
||||
list.forEach { entity ->
|
||||
val tag = entity.sort.toString()
|
||||
val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新网格数据
|
||||
*/
|
||||
private fun updateGridData(entity: SeasoningEntity) {
|
||||
val gridLayout = binding.include.root
|
||||
val tag = entity.sort.toString()
|
||||
val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击网格项,打开调料搜索对话框
|
||||
*/
|
||||
private fun clickGridItem(sort: Int, entity: SeasoningEntity) {
|
||||
val currentAddress = AddressUtil.getWeighAddressArray().get(sort)
|
||||
SeasoningSearchDialog(
|
||||
activity = requireActivity() as BaseActivity,
|
||||
weighIndex = sort,
|
||||
weighAddress = currentAddress,
|
||||
clickName = entity.goodsName
|
||||
) { item ->
|
||||
syncItem(entity, item)
|
||||
// 3-调料
|
||||
entity.materialType = 3
|
||||
entity.sort = sort
|
||||
saveSeasoning(entity) {
|
||||
val tag = sort.toString()
|
||||
val gridLayout = binding.include.root
|
||||
val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
}
|
||||
}.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步调料项数据
|
||||
*/
|
||||
private fun syncItem(oldItem: SeasoningEntity, newItem: SeasoningEntity) {
|
||||
oldItem.run {
|
||||
goodsId = newItem.goodsId
|
||||
goodsName = newItem.goodsName
|
||||
goodsOrRelationCode = newItem.goodsOrRelationCode
|
||||
relateionType = newItem.relateionType
|
||||
materialType = newItem.materialType
|
||||
allEdible = newItem.allEdible
|
||||
useWeight = newItem.useWeight
|
||||
popularName = newItem.popularName
|
||||
canteenId = newItem.canteenId
|
||||
relateionType_dictText = newItem.relateionType_dictText
|
||||
|
||||
foodId = newItem.foodId
|
||||
sort = newItem.sort
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存调料数据
|
||||
*/
|
||||
fun saveSeasoning(entity: SeasoningEntity, action: () -> Unit = {}) {
|
||||
appViewModel.saveSeasoning(entity) {
|
||||
action()
|
||||
toast("保存成功")
|
||||
}
|
||||
}
|
||||
//
|
||||
// /**
|
||||
// * 添加称重监听
|
||||
// */
|
||||
// private fun addWeighListener() {
|
||||
// WeightUtil.addWeightListener(
|
||||
// weightKey = TAG,
|
||||
// getWeight = { address, state, weight ->
|
||||
// Log.d(TAG, "addWeighListener: address=$address,stat=$state,weight=$weight")
|
||||
// weightArray.put(address, weight)
|
||||
// val item = seasoningItems.firstOrNull { address == addressArray[it.sort] }
|
||||
// item?.let {
|
||||
// it.useWeight = weight.toDouble()
|
||||
// updateGridData(it)
|
||||
// }
|
||||
// Log.d(TAG, "addWeighListener: update-----------------------")
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 添加网格项点击监听
|
||||
// */
|
||||
// private fun addGridItemListener() {
|
||||
// binding.include.root.forEach { child ->
|
||||
// child.clickWithDebounce {
|
||||
// val sort = child.tag.toString().toInt()
|
||||
// val entity = seasoningItems.firstOrNull { it.sort == sort } ?: SeasoningEntity()
|
||||
// clickGridItem(sort, entity)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 加载调料数据
|
||||
// */
|
||||
// private fun loadSeasoning() {
|
||||
//// appViewModel.loadSeasoning {
|
||||
//// seasoningItems.clear()
|
||||
//// seasoningItems.addAll(it)
|
||||
//// initConfigData()
|
||||
//// setGridData(seasoningItems)
|
||||
//// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 初始化配置数据,确保有12个调料位置
|
||||
// */
|
||||
// private fun initConfigData() {
|
||||
// repeat(12) { num ->
|
||||
// val firstOne = seasoningItems.firstOrNull { it.sort == num }
|
||||
// if (firstOne == null) {
|
||||
// seasoningItems.add(SeasoningEntity().also { it.sort = num })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 设置网格数据
|
||||
// */
|
||||
// private fun setGridData(list: MutableList<SeasoningEntity>) {
|
||||
// val gridLayout = binding.include.root
|
||||
// list.forEach { entity ->
|
||||
// val tag = entity.sort.toString()
|
||||
// val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
// TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 更新网格数据
|
||||
// */
|
||||
// private fun updateGridData(entity: SeasoningEntity) {
|
||||
// val gridLayout = binding.include.root
|
||||
// val tag = entity.sort.toString()
|
||||
// val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
// TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 点击网格项,打开调料搜索对话框
|
||||
// */
|
||||
// private fun clickGridItem(sort: Int, entity: SeasoningEntity) {
|
||||
// val currentAddress = AddressUtil.getWeighAddressArray().get(sort)
|
||||
// SeasoningSearchDialog(
|
||||
// activity = requireActivity() as BaseActivity,
|
||||
// weighIndex = sort,
|
||||
// weighAddress = currentAddress,
|
||||
// clickName = entity.goodsName
|
||||
// ) { item ->
|
||||
// syncItem(entity, item)
|
||||
// // 3-调料
|
||||
// entity.materialType = 3
|
||||
// entity.sort = sort
|
||||
// saveSeasoning(entity) {
|
||||
// val tag = sort.toString()
|
||||
// val gridLayout = binding.include.root
|
||||
// val frameLayout = gridLayout.findViewWithTag<FrameLayout>(tag)
|
||||
// TextCellAdapter.loadLayout(frameLayout, entity)
|
||||
// }
|
||||
// }.show()
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 同步调料项数据
|
||||
// */
|
||||
// private fun syncItem(oldItem: SeasoningEntity, newItem: SeasoningEntity) {
|
||||
// oldItem.run {
|
||||
// goodsId = newItem.goodsId
|
||||
// goodsName = newItem.goodsName
|
||||
// goodsOrRelationCode = newItem.goodsOrRelationCode
|
||||
// relateionType = newItem.relateionType
|
||||
// materialType = newItem.materialType
|
||||
// allEdible = newItem.allEdible
|
||||
// useWeight = newItem.useWeight
|
||||
// popularName = newItem.popularName
|
||||
// canteenId = newItem.canteenId
|
||||
// relateionType_dictText = newItem.relateionType_dictText
|
||||
//
|
||||
// foodId = newItem.foodId
|
||||
// sort = newItem.sort
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 保存调料数据
|
||||
// */
|
||||
// fun saveSeasoning(entity: SeasoningEntity, action: () -> Unit = {}) {
|
||||
// appViewModel.saveSeasoning(entity) {
|
||||
// action()
|
||||
// toast("保存成功")
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 加载品控模式文本样式
|
||||
@@ -359,6 +347,6 @@ class DeviceConfigFragment : BaseFragment<FragmentDeviceConfigBinding>() {
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
// 移除称重监听
|
||||
WeightUtil.removeWeightListener(TAG)
|
||||
// WeightUtil.removeWeightListener(TAG)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
package com.shuwei.dish.match.ui.fragment
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
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.Seasoning18GridAdapter
|
||||
import com.shuwei.dish.match.adapter.Seasoning22GridAdapter
|
||||
import com.shuwei.dish.match.base.BaseFragment
|
||||
import com.shuwei.dish.match.base.DeviceRole
|
||||
import com.shuwei.dish.match.base.GlobalData
|
||||
import com.shuwei.dish.match.databinding.FragmentSeasoningConfigBinding
|
||||
import com.shuwei.dish.match.dialog.SeasoningSelectDialog
|
||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||
import com.shuwei.dish.match.scale.ScaleEvent
|
||||
import com.shuwei.dish.match.scale.ScaleServiceManager
|
||||
import com.shuwei.dish.match.ui.SettingActivity
|
||||
import com.shuwei.dish.match.utils.WeightUtil
|
||||
import com.shuwei.dish.match.utils.ext.dp
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SeasoningConfigFragment"
|
||||
/** 重量增加阈值(克),超过此值才触发高亮 */
|
||||
private const val WEIGHT_DELTA_THRESHOLD = 10.0
|
||||
/** 高亮自动恢复时间(毫秒) */
|
||||
private const val HIGHLIGHT_DURATION_MS = 15_000L
|
||||
}
|
||||
|
||||
override fun inflateBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
) = FragmentSeasoningConfigBinding.inflate(inflater, container, false)
|
||||
|
||||
private lateinit var settingActivity: SettingActivity
|
||||
// private lateinit var appViewModel: AppViewModel
|
||||
|
||||
private val adapter22 by lazy { buildAdapter22() }
|
||||
private val adapter18 by lazy { Seasoning18GridAdapter() }
|
||||
|
||||
/** 各秤地址的上一次重量快照,用于计算 delta */
|
||||
private val weightSnapshot = mutableMapOf<Int, Double>()
|
||||
/** 主线程 Handler,用于延迟清除高亮 */
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
/** 当前高亮清除任务,新高亮触发时取消旧任务 */
|
||||
private var clearHighlightRunnable: Runnable? = null
|
||||
|
||||
override fun initialize() {
|
||||
settingActivity = activity as SettingActivity
|
||||
// initViewModel()
|
||||
setupRecyclerViews()
|
||||
bindAdapterClicks()
|
||||
addWeightListener()
|
||||
loadData()
|
||||
}
|
||||
|
||||
/** 绑定两个 Adapter 的格子点击,弹出调料选择弹窗 */
|
||||
private fun bindAdapterClicks() {
|
||||
adapter22.onItemClick = { _, position ->
|
||||
SeasoningSelectDialog(
|
||||
activity = settingActivity,
|
||||
clickIndex = position,
|
||||
onItemSelected = { item ->
|
||||
adapter22.updateItemName(position, item.goodsName ?: "")
|
||||
}
|
||||
).show()
|
||||
}
|
||||
adapter18.onItemClick = { _, position ->
|
||||
SeasoningSelectDialog(
|
||||
activity = settingActivity,
|
||||
clickIndex = position,
|
||||
onItemSelected = { item ->
|
||||
adapter18.updateItemName(position, item.goodsName ?: "")
|
||||
}
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
// /** 初始化 ViewModel */
|
||||
// private fun initViewModel() {
|
||||
// val factory = AppFactory(AppRepository(BaseApp.instance!!.database.appDao()))
|
||||
// appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
|
||||
// }
|
||||
|
||||
/** 构建上层(22格)Adapter,需要先计算格子尺寸 */
|
||||
private fun buildAdapter22(): Seasoning22GridAdapter {
|
||||
val usable = resources.displayMetrics.widthPixels - 8 * 4.dp - 10.dp
|
||||
val smallSize = usable / 9
|
||||
val largeSize = (usable - smallSize * 6) / 2
|
||||
return Seasoning22GridAdapter(largeSize, smallSize)
|
||||
}
|
||||
|
||||
/** 配置两个 RecyclerView */
|
||||
private fun setupRecyclerViews() {
|
||||
// 上层:FlexboxLayoutManager,仿 MasterScaleActivity 中 Scale22VH 的布局方式
|
||||
// rvTop 高度 = 小格子尺寸 * 3行 + 3 * 间距,与 Scale22VH 保持一致
|
||||
val screenWidth = resources.displayMetrics.widthPixels
|
||||
val usable = screenWidth - 8 * 4.dp - 10.dp
|
||||
val smallSize = usable / 9
|
||||
val rvTopHeight = smallSize * 3 + 3 * 4.dp
|
||||
binding.rvTop.apply {
|
||||
layoutParams = layoutParams.also { it.height = rvTopHeight }
|
||||
layoutManager = FlexboxLayoutManager(requireContext()).apply {
|
||||
flexDirection = FlexDirection.COLUMN
|
||||
flexWrap = FlexWrap.WRAP
|
||||
alignItems = AlignItems.FLEX_START
|
||||
}
|
||||
itemAnimator = null
|
||||
adapter = adapter22
|
||||
}
|
||||
|
||||
// 下层:GridLayoutManager 6列,仿 Scale18VH
|
||||
binding.rvBottom.apply {
|
||||
layoutManager = GridLayoutManager(requireContext(), 6)
|
||||
itemAnimator = null
|
||||
adapter = adapter18
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载默认空数据,使两个列表完整展示所有格子 */
|
||||
private fun loadData() {
|
||||
adapter22.update(emptyList())
|
||||
adapter18.update(emptyList())
|
||||
// appViewModel.loadSeasoning { list ->
|
||||
// adapter22.update(list ?: emptyList())
|
||||
// adapter18.update(list ?: emptyList())
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册重量监听,检测重量增加超过阈值的秤,高亮对应格子并通知主设备
|
||||
*
|
||||
* 策略:
|
||||
* - MASTER 设备:订阅 ScaleServiceManager.allScales StateFlow,覆盖本机+远端所有秤数据
|
||||
* - SLAVE 设备:注册 WeightUtil 监听(仅本机秤)并向主设备广播事件
|
||||
*
|
||||
* 基准更新策略:
|
||||
* - 首次收到数据:直接记录基准,不触发高亮
|
||||
* - delta > 阈值:触发高亮,同时更新基准防止重复触发
|
||||
* - STATE_STABLE:无条件更新基准(覆盖取走调料/归零等场景)
|
||||
*/
|
||||
private fun addWeightListener() {
|
||||
Log.d(TAG, "addWeightListener: 注册监听, deviceId=${GlobalData.deviceId}, role=${GlobalData.deviceRole}")
|
||||
|
||||
if (GlobalData.deviceRole == DeviceRole.MASTER) {
|
||||
// 主设备:通过 allScales StateFlow 观测所有秤(含远端子设备秤)
|
||||
val allScalesFlow = ScaleServiceManager.allScales
|
||||
if (allScalesFlow == null) {
|
||||
Log.w(TAG, "allScales 为 null,主设备聚合器未启动")
|
||||
return
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
allScalesFlow.collect { scaleMap ->
|
||||
scaleMap.forEach { (key, data) ->
|
||||
val snapshotKey = data.address + data.deviceId.hashCode() * 1000
|
||||
val baseline = weightSnapshot[snapshotKey]
|
||||
if (baseline == null) {
|
||||
// 首次收到:记录基准
|
||||
weightSnapshot[snapshotKey] = data.weight
|
||||
return@forEach
|
||||
}
|
||||
val delta = data.weight - baseline
|
||||
Log.d(TAG, "allScales: key=$key address=${data.address} state=${data.state} weight=${data.weight} baseline=$baseline delta=$delta")
|
||||
when {
|
||||
// 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发
|
||||
data.state == WeightUtil.STATE_STABLE && delta > WEIGHT_DELTA_THRESHOLD -> {
|
||||
weightSnapshot[snapshotKey] = data.weight
|
||||
Log.d(TAG, "allScales: 触发高亮 key=$key address=${data.address} delta=$delta")
|
||||
triggerHighlight(data.deviceId, data.address, delta)
|
||||
}
|
||||
data.state == WeightUtil.STATE_STABLE -> {
|
||||
weightSnapshot[snapshotKey] = data.weight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 子设备:注册本机 WeightUtil 监听,触发后通过 broadcastEvent 通知主设备
|
||||
WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
||||
val baseline = weightSnapshot[address]
|
||||
Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline")
|
||||
|
||||
if (baseline == null) {
|
||||
weightSnapshot[address] = weight
|
||||
return@addWeightListener
|
||||
}
|
||||
|
||||
val delta = weight - baseline
|
||||
Log.d(TAG, "onWeight: address=$address state=$state weight=$weight baseline=$baseline delta=$delta")
|
||||
|
||||
when {
|
||||
// 仅在秤稳定后判断增量,避免取走物品时手部冲力造成误触发
|
||||
state == WeightUtil.STATE_STABLE && delta > WEIGHT_DELTA_THRESHOLD -> {
|
||||
weightSnapshot[address] = weight
|
||||
Log.d(TAG, "onWeight: 触发高亮 address=$address delta=$delta")
|
||||
triggerHighlight(GlobalData.deviceId, address, delta)
|
||||
}
|
||||
state == WeightUtil.STATE_STABLE -> {
|
||||
weightSnapshot[address] = weight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据来源设备ID和秤地址触发对应格子高亮,15 秒后自动恢复
|
||||
* 通过 sourceDeviceId 精确匹配 SCALE_ORDER,解决 18格/22格 地址重叠问题
|
||||
* @param sourceDeviceId 产生重量变化的设备 ID
|
||||
* @param address 秤硬件地址
|
||||
* @param delta 重量变化量(克)
|
||||
*/
|
||||
private fun triggerHighlight(sourceDeviceId: String, address: Int, delta: Double) {
|
||||
// 根据来源设备决定查哪个列表,而非查本机设备ID
|
||||
val pos22 = if (sourceDeviceId != ScaleDeviceConfig.DEVICE_ID_18)
|
||||
ScaleDeviceConfig.SCALE_ORDER_22.indexOf(address) else -1
|
||||
val pos18 = if (sourceDeviceId != ScaleDeviceConfig.DEVICE_ID_22)
|
||||
ScaleDeviceConfig.SCALE_ORDER_18.indexOf(address) else -1
|
||||
|
||||
Log.d(TAG, "triggerHighlight: sourceDeviceId=$sourceDeviceId address=$address pos22=$pos22 pos18=$pos18")
|
||||
|
||||
// 两个列表都不包含该地址,忽略
|
||||
if (pos22 < 0 && pos18 < 0) {
|
||||
Log.d(TAG, "triggerHighlight: address=$address 不在任何 SCALE_ORDER 中,忽略")
|
||||
return
|
||||
}
|
||||
|
||||
mainHandler.post {
|
||||
clearHighlightRunnable?.let { mainHandler.removeCallbacks(it) }
|
||||
|
||||
when {
|
||||
pos22 >= 0 -> {
|
||||
adapter22.highlightPosition(pos22)
|
||||
adapter18.clearHighlight()
|
||||
}
|
||||
else -> {
|
||||
adapter18.highlightPosition(pos18)
|
||||
adapter22.clearHighlight()
|
||||
}
|
||||
}
|
||||
|
||||
clearHighlightRunnable = Runnable {
|
||||
adapter22.clearHighlight()
|
||||
adapter18.clearHighlight()
|
||||
}.also { mainHandler.postDelayed(it, HIGHLIGHT_DURATION_MS) }
|
||||
|
||||
// 子设备:通知主设备(携带本机 deviceId 和地址)
|
||||
if (GlobalData.deviceRole == DeviceRole.SLAVE) {
|
||||
ScaleServiceManager.broadcastEvent(
|
||||
ScaleEvent(
|
||||
type = ScaleEvent.TYPE_SEASONING_ADDED,
|
||||
deviceId = GlobalData.deviceId,
|
||||
address = address,
|
||||
delta = delta
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
// 子设备才注册了 WeightUtil 监听,移除时安全无副作用
|
||||
WeightUtil.removeWeightListener(TAG)
|
||||
ScaleServiceManager.onScaleEvent = null
|
||||
clearHighlightRunnable?.let { mainHandler.removeCallbacks(it) }
|
||||
}
|
||||
}
|
||||
@@ -31,4 +31,10 @@ object SpTool {
|
||||
fun getBoolean(key: String, defValue: Boolean = false): Boolean {
|
||||
return pref.getBoolean(key, defValue)
|
||||
}
|
||||
|
||||
var cookMode: Int
|
||||
get() = getInt(LAUNCH_PAGE_TYPE, -1)
|
||||
set(value) {
|
||||
put(LAUNCH_PAGE_TYPE, value)
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,20 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_weight="1"
|
||||
android:text="设备配置"
|
||||
android:text="设备切换"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnSeasoningConfig"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_weight="1"
|
||||
android:text="调料配置"
|
||||
android:textSize="26sp"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/btnFoodCollect"
|
||||
android:layout_width="0dp"
|
||||
@@ -53,7 +62,7 @@
|
||||
android:layout_weight="1"
|
||||
android:text="秤数据监控"
|
||||
android:textSize="26sp"
|
||||
android:visibility="gone"
|
||||
android:visibility="visible"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?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="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
tools:background="@color/white"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/viewLine"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:background="@drawable/shape_gray_dc_5" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginStart="60dp"
|
||||
android:layout_marginEnd="60dp"
|
||||
android:background="@drawable/shape_white_e6_12_corners"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSheetInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="@null"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:imeOptions="actionSearch"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"
|
||||
android:paddingStart="30dp"
|
||||
android:paddingEnd="30dp"
|
||||
android:textColor="@color/black666"
|
||||
android:textColorHint="@color/gray_c8"
|
||||
android:textSize="36sp"
|
||||
tools:hint="@string/input_seasoning_name"
|
||||
tools:ignore="Autofill,LabelFor,TextFields" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivSearch"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_search_gray"
|
||||
tools:ignore="ContentDescription" />
|
||||
</FrameLayout>
|
||||
|
||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||
android:id="@+id/refreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
app:srlEnableOverScrollDrag="false">
|
||||
|
||||
<com.scwang.smart.refresh.header.ClassicsHeader
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="45dp"
|
||||
android:layout_marginEnd="45dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:minHeight="380dp"
|
||||
android:nestedScrollingEnabled="true"
|
||||
android:overScrollMode="never"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="2"
|
||||
tools:itemCount="10"
|
||||
tools:listitem="@layout/list_item_search_goods_info" />
|
||||
|
||||
<com.scwang.smart.refresh.footer.ClassicsFooter
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -10,8 +10,7 @@
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="600dp"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:background="@drawable/shape_white_30_corners"
|
||||
@@ -100,7 +99,8 @@
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginEnd="30dp">
|
||||
android:layout_marginEnd="30dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/bg_color">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="上层"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/home_title"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginBottom="6dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvTop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="下层"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/home_title"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="6dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvBottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -3,12 +3,14 @@
|
||||
<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_height="120dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
android:paddingBottom="8dp"
|
||||
android:background="@drawable/shape_scale_cell">
|
||||
|
||||
<!-- 秤编号 -->
|
||||
<TextView
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 调料配置格子:仅显示调料名称 -->
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tvGoodsName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:padding="4dp"
|
||||
android:background="@drawable/shape_scale_cell"
|
||||
android:textColor="@color/home_title" />
|
||||
@@ -29,5 +29,6 @@
|
||||
<color name="black333">#333333</color>
|
||||
<color name="gray_c8">#C8C8C8</color>
|
||||
<color name="gray_e6">#E6E6E6</color>
|
||||
<color name="red_ff4444">#FF4444</color>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user